iOS - UIButton image doesn't fit as expected - ios

I have a UIButton that has a default image and if a property is set it changes to the property object (an image);
With the default image, everything works properly and the image fits perfectly but when I'm using the image, I don't know why the image doesn't fit.
Here's the code:
var user: User? {
didSet {
self.profileImageButton.setImage(self.user?.profileImage?.withRenderingMode(.alwaysOriginal) ?? #imageLiteral(resourceName: "plus_photo").withRenderingMode(.alwaysOriginal), for: .normal)
}
}
lazy var profileImageButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "plus_photo").withRenderingMode(.alwaysOriginal), for: .normal)
button.layer.cornerRadius = 80/2
button.contentMode = .scaleAspectFill
button.layer.masksToBounds = true
button.clipsToBounds = true
button.layer.borderWidth = 1
button.layer.borderColor = .white
button.addTarget(self, action: #selector(didTapPhoto), for: .touchUpInside)
return button
}()
I'm expecting something like this
But I'm getting this instead

Add this line to your button configuration
button.imageView?.contentMode = .scaleAspectFit

Related

UIButton title and image positions are swapped unexpectedly on clicked

I'd like to make a button that the image is on the right instead of the left side.
This is the expected layout:
I'm able to satisfy the layouts with the following code:
import UIKit
class ViewController: UIViewController {
private lazy var leftButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
button.setImage(UIImage(systemName: "trash"), for: .normal)
button.setTitle("Trash", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
if let imageView = button.imageView {
imageView.contentMode = .scaleAspectFit
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalToConstant: 8),
imageView.heightAnchor.constraint(equalToConstant: 8)
])
}
button.contentHorizontalAlignment = .left
button.contentVerticalAlignment = .center
button.semanticContentAttribute = .forceRightToLeft
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: leftButton)
}
#objc private func didTap() {
}
}
Everything looked exactly what I wanted at the beginning until I clicked the button. The positions of the title and the image were swapped. Please take a look at the gif:
Does anyone have any clue on why this happened? And how can I improve my code?
I guess it may have something to do with the navigation item.
Try it like this:
let button = UIButton(type: .system)
button.setTitle("Trash", for: .normal)
button.setImage(UIImage(systemName: "trash"), for: .normal)
button.setPreferredSymbolConfiguration(
.init(scale: .small), forImageIn: .normal
)
button.widthAnchor.constraint(equalToConstant: 60).isActive = true
button.imageEdgeInsets.right = -75
button.titleEdgeInsets.left = -35
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
However, if you can confine yourself to iOS 15 and later, this is a lot more simple and coherent, because the notion of a trailing image is built right in:
var config = UIButton.Configuration.plain()
config.title = "Trash"
config.image = UIImage(systemName: "trash")?
.applyingSymbolConfiguration(.init(scale: .small))
config.imagePlacement = .trailing
let button = UIButton(configuration: config)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)

UIButton titleLabel has padding

UIButton title label has top and bottom padding, I want to remove padding.
Set UIButton content mode did not work.
Here is my code
lazy var button: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(ThemeColor.red, for: .normal)
button.setTitle("Push", for: .normal)
button.addTarget(self, action: #selector(buttonDidTap), for: .touchUpInside)
button.backgroundColor = .blue
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.contentMode = .scaleAspectFill
return button
}()
and it looks like
How can I remove the padding space!
As Matt pointed out, you can fix this by adjusting the button's contentEdgeInsets
However, one thing I noticed, if you set the contentEdgeInsets to 0 all around:
button.contentEdgeInsets = UIEdgeInsets(top: 0,
left: 0,
bottom: 0,
right: 0)
You still get the the vertical padding for some reason.
I remember seeing an answer which I cannot find now where it suggested to set an extremely small edge inset and this should work:
lazy var button: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(.red, for: .normal)
button.setTitle("Push", for: .normal)
button.backgroundColor = .blue
button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.contentMode = .scaleAspectFill
button.contentEdgeInsets = UIEdgeInsets(top: .leastNormalMagnitude,
left: .leastNormalMagnitude,
bottom: .leastNormalMagnitude,
right: .leastNormalMagnitude)
return button
}()
You can use UIButton.Configuration and then set its contentInsets to .zero
lazy var button: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
var configuration = UIButton.Configuration.plain()
configuration.background.backgroundColor = .blue
configuration.background.cornerRadius = 0
configuration.baseForegroundColor = .red
configuration.title = "Push"
configuration.contentInsets = .zero
button.configuration = configuration
return button
}()
You can use the button's configuration to get more precise control over its appearance like so:
lazy var myButton: UIButton = {
let newButton = UIButton()
newButton.translatesAutoresizingMaskIntoConstraints = false
newButton.contentMode = .scaleAspectFill
newButton.addTarget(self, action: #selector(buttonDidTap), for: .touchUpInside)
// 'configurationUpdateHandler' property can be used to set appearance depending on its state
newButton.configurationUpdateHandler = { button in
switch button.state { // here i'll just use default so it's the same over all states
default:
button.configuration?.title = "Push"
button.configuration?.baseBackgroundColor = .blue
// remove padding here
button.configuration?.contentInsets.top = 0
button.configuration?.contentInsets.bottom = 0
}
}
return newButton
}()
Of course you don't need to use updateHandler, you can just access the configuration directly and just set it there
button.configuration?.contentInsets.top = 0
button.configuration?.contentInsets.bottom = 0
See if this solves the problem...

UIButton Function add Tap Style

I Have written a function in Swift 4 which creates a button that receives the UIButton, a Color and a String for the title. I would like to add to this function a highlight or tap style but I am not sure how.
The function:
func homeBtn(button: UIButton, color: UIColor, title: String){
button.setTitle(title, for: .normal)
button.setTitleColor(color, for: .normal)
button.titleLabel?.font = UIFont(name: "Raleway-Medium", size: 24)
button.titleLabel?.lineBreakMode = .byWordWrapping
button.titleLabel?.textAlignment = .center
button.titleEdgeInsets = UIEdgeInsetsMake(20,20,20,20)
button.layer.borderColor = color.cgColor
button.layer.borderWidth = 2
button.layer.cornerRadius = button.frame.width/2
button.layer.backgroundColor = whiteColor.cgColor
}
The button is a circle and has a String centered in the middle with the UIColor being used as an outline. Can someone show me how to add a tap style to this function that uses the same UIColor to fill in the button and turn the text white please?
For the title color you can easily do this without adding events.
button.setTitleColor(.white, for: .highlighted)
button.setTitleColor(.black, for: .normal)
For the background color you need to define the following events:
button.addTarget(self, action: #selector(touchDown(button:)), for: .touchDown)
button.addTarget(self, action: #selector(touchUpInside(button:)), for: .touchUpInside)
Define functions - You can refer additional touch types as well - see UIControlEvents
#objc func touchDown(button: UIButton) {
guard let borderColor = button.layer.borderColor else {
return
}
button.backgroundColor = UIColor(cgColor: borderColor)
}
#objc func touchUpInside(button: UIButton) {
// Set backgroundColor for normal state...
}

View transform rotation nullify image rendering effects in iOS

I've created a UIButton as follows (round button with shadow properties nothing too fancy):
let button = UIButton(type: .system)
button.backgroundColor = UIColor(hexString: "4267B2")
button.tintColor = .white
button.setImage(#imageLiteral(resourceName: "add").withRenderingMode(.alwaysTemplate), for: .normal)
button.addTarget(self, action: #selector(handleClick), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
button.layer.cornerRadius = 32
button.layer.shadowColor = UIColor.darkGray.cgColor
button.layer.shadowRadius = 6
button.layer.shadowOpacity = 0.6
button.layer.shadowOffset = CGSize.zero
Purpose is to seem as an add button on click it rotates to form a cross button.
func handleClick() {
button.isSelected = !newPostButton.isSelected
let rotationDirection: CGFloat = button.isSelected ? 1: -1
UIView.animate(withDuration: 0.5) {
self.button.transform = self.button.transform.rotated(by: (rotationDirection * .pi/4))
}
}
The issue I encounter is when the animation occur for rotation the rotation works perfectly but the image provided for button .withRendering: is nullified as shown below:
Is there a fix? (with code no libraries please) I searched through the net but none had a similar issue.
You could set your button type to .custom instead of .system. This should fix your problem.

How to set the color of the image view in UIButton on type Custom?

I've tried everything and nothing is happening. It just stays black. Am I missing something? I tried using the UIButton's image view but it doesn't make a change.
lazy var addFriendButton: UIButton = {
self.friendButton = UIButton(type: .Custom)
self.friendButton.bounds = CGRectMake(0, 0, 120, 80)
self.friendButton.backgroundColor = UIColor.redColor()
self.friendButton.translatesAutoresizingMaskIntoConstraints = false
self.friendButton.setTitle("Add Friend", forState: .Normal)
self.friendButton.titleLabel?.font = UIFont(name: "HelveticaNeue", size: 12)
self.friendButton.addTarget(self, action: #selector(addFriend), forControlEvents: .TouchUpInside)
let addButtonImage = UIImage(named: "AddFriend")
addButtonImage?.imageWithRenderingMode(.AlwaysTemplate)
self.friendButton.setImage(addButtonImage, forState: .Normal)
self.friendButton.setTitleColor(UIColor.darkGrayColor(), forState: .Normal)
self.friendButton.imageView?.image = addButtonImage
// Not working here
self.friendButton.imageView?.tintColor = UIColor.whiteColor()
return self.friendButton
}()
As it says in the documentation:
- withRenderingMode(_:)
Creates and returns a new image object with the specified rendering mode.
So just writing addButtonImage?.withRenderingMode(.alwaysTemplate) doesn't change the addButtonImage. The way to use it would be like this:
var addButtonImage = UIImage(named: "AddFriend")
addButtonImage = addButtonImage?.withRenderingMode(.alwaysTemplate)
or you can simply use this one line:
let addButtonImage = UIImage(named: "AddFriend")?.withRenderingMode(.alwaysTemplate)
Sam_M's answer updated to Swift 3:
let addButtonImage = UIImage(named: "AddFriend")?.withRenderingMode(.alwaysTemplate)

Resources