Why is space added between UIButtons? - ios

I have the following code
let mostRead = UIButton(frame: CGRect(x: 0, y: tableView.frame.origin.y + tableView.frame.height, width: self.view.bounds.size.width, height: 160))
mostRead.setImage(UIImage.init(named: "mostread"), for: .normal)
mostRead.addTarget(self, action:#selector(self.goToMostRead(sender:)), for: .touchUpInside)
innerView.addSubview(mostRead)
let videoTitles = UIButton(frame: CGRect(x: 0, y: mostRead.frame.maxY, width: self.view.bounds.size.width, height: 160))
videoTitles.setImage(UIImage.init(named: "videotitles"), for: .normal)
videoTitles.addTarget(self, action:#selector(self.goToVideoTitles(sender:)), for: .touchUpInside)
innerView.addSubview(videoTitles)
let audioTitles = UIButton(frame: CGRect(x: 0, y: videoTitles.frame.maxY, width: self.view.bounds.size.width, height: 160))
audioTitles.setImage(UIImage.init(named: "audiotitles"), for: .normal)
audioTitles.addTarget(self, action:#selector(self.goToAudioTitles(sender:)), for: .touchUpInside)
innerView.addSubview(audioTitles)
Space gets added between the UI Buttons. If you notice in the picture, there's a lot of space between each image even though I specified no space between images. Does anyone know why that's happening?

You try to add it with stackView and auto layout like this:
first set your object under your controller class:
let buttonmostRead: UIButton = {
let button = UIButton()
button.backgroundColor = .red
button.setBackgroundImage(UIImage(named: "mostRead"), for: .normal)
button.layer.cornerRadius = 8
button.clipsToBounds = true
return button
}()
let buttonvideoTitles: UIButton = {
let button = UIButton()
button.backgroundColor = .red
button.setBackgroundImage(UIImage(named: "videoTitles"), for: .normal)
button.layer.cornerRadius = 8
button.clipsToBounds = true
return button
}()
let buttonaudiotitles: UIButton = {
let button = UIButton()
button.backgroundColor = .red
button.setBackgroundImage(UIImage(named: "audiotitles"), for: .normal)
button.layer.cornerRadius = 8
button.clipsToBounds = true
return button
}()
after that in viewDiLoad set staclView an constraints:
let stackView = UIStackView(arrangedSubviews: [buttonmostRead, buttonvideoTitles, buttonaudiotitles])
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 8
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.heightAnchor.constraint(equalToConstant: 466).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
to set space between button, change stackView spacing...
and this is the result
I put stackView in the center of the view but you can place them wherever you want by changing the constraints...

Related

Set Constraints to Labels So That One is At Leading Side and The Other at Trailing Side in Swift

I am programmatically adding a label and a button in Swift. I want the label to be at leading side and the button at trailing side. This is what I did so far:
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: 50))
view.clipsToBounds = true
view.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
button.frame = CGRect.init(x: 100, y: 0, width: view.frame.width-25, height: view.frame.height-25)
label.frame = CGRect.init(x: 10 , y: 0, width: view.frame.width-10, height: view.frame.height-10)
view.addSubview(label)
view.addSubview(button)
And this results in:
My desired result is:
I want see all to be at trailing side and the top-rated label at the leading side. How can I achieve it in Swift?
Question is a little unclear, I think you are asking how to achieve the same layout with constraints instead of using frames?
Here is a rough sample of how to do it, you might need to tweak the constants:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let view = UIView(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: 50))
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
view.addSubview(label)
view.addSubview(button)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
label.trailingAnchor.constraint(equalTo: button.leadingAnchor, constant: 16),
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 16),
button.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16)
])
self.view.addSubview(view)
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 21, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
let stack = UIStackView(arrangedSubviews: [label, button])
stack.axis = .horizontal
stack.distribution = .fill
stack.alignment = .center
stack.spacing = 16
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
let padding: CGFloat = 16
let viewHeight: CGFloat = 50
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: padding),
stack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: padding),
stack.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 1-padding),
stack.heightAnchor.constraint(equalToConstant: viewHeight)
])
}

Swift - UIView moves when changing tab controller tabs

Inside the viewDidAppear I have a function that contains this code, in order to make a UIView:
let contentView = UIView()
func addSleepingView() {
contentView.backgroundColor = .systemYellow.withAlphaComponent(0.25)
view.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
// Anchor your view right above the tabBar
contentView.bottomAnchor.constraint(equalTo: (tabBarController?.tabBar.topAnchor)!).isActive = true
contentView.heightAnchor.constraint(equalToConstant: 50).isActive = true
let label = UILabel()
label.text = "Test"
label.frame = CGRect(x: 0, y: 0, width: 25, height: 34.0)
label.lineBreakMode = .byWordWrapping
label.numberOfLines = 0
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(label)
label.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
let button = UIButton()
button.setImage(UIImage(systemName: "arrow.clockwise", withConfiguration: UIImage.SymbolConfiguration(scale: .large)), for: UIControl.State.normal)
button.tintColor = .systemGray
button.frame = CGRect(x: self.view.bounds.width-42, y: 8, width: 34, height: 34.0)
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(wakeupFunction), for: .touchUpInside)
contentView.addSubview(button)
let button2 = UIButton()
button2.setImage(UIImage(systemName: "exclamationmark.triangle", withConfiguration: UIImage.SymbolConfiguration(scale: .large)), for: UIControl.State.normal)
button2.tintColor = .label
button2.frame = CGRect(x: 8, y: 8, width: 34, height: 34.0)
button2.isUserInteractionEnabled = false
contentView.addSubview(button2)
contentView.bringSubviewToFront(button)
}
This is what it looks like:
Now this is exactly how I want it. The problem comes when I change tab. For example go to the last tab, and back to the first tab again. Then it looks like this:
What am I doing wrong here?
You can just add your code of activating constraints inside the DispatchQueue.main block
Updated Line you can change and it will start working as you're expectation.
// Anchor your view right above the tabBar
DispatchQueue.main.async
{
self.contentView.bottomAnchor.constraint(equalTo: (self.tabBarController?.tabBar.topAnchor)!).isActive = true
self.contentView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}

SetImage() method removes titleLabel for UIButton

My wish is to make centered image(left) and next to it(right) the label.
Without setting an image, there was a perfectly centered titleLabel:
btnWhatsapp.titleLabel?.adjustsFontSizeToFitWidth = true
btnWhatsapp.setTitle("WhatsApp", for: .normal)
Then I added this code to add an image:
btnWhatsapp.setImage(UIImage(named: "phoneIcon"), for: .normal)
btnWhatsapp.imageView?.layer.transform = CATransform3DMakeScale(0.5, 0.6, 0.5)
btnWhatsapp.imageView?.contentMode = .scaleAspectFit
, and this iswhat I got then:
, so the title disappeared.
Maybe the problem is that image uses more space than its actual size(the size shouldnt take more widht and height than the icon size). I saw this when changed images background(should be this much grey color):
btnWhatsapp.imageView?.backgroundColor = .gray
I tried to use the imageEdgeInsets but it is very hard to calculate it to fit perfectly on every iPhone.
This is the Attributes inspector of the button:
You can't set title and image at once by default, nor position them as you describe.
If you need to have a UIButton, I'd recommend to make a UIView (or possibly horizontal UIStackView) with UIImage and UILabel inside, position them with autolayout, then you can add this view to the UIButton as a subview.
let button = UIButton(type: .custom)
button.frame = viewFrame // This is the desired frame of your custom UIView or UIStackView
button.addSubview(customView)
You will be able to position the views easily for all sizes with this approach, but you will probably want to use autolayout in real word app, instead of hardcoded frames.
Example:
override func viewDidLoad() {
super.viewDidLoad()
let image = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
label.text = "text"
let stack = UIStackView(arrangedSubviews: [image, label])
stack.frame = CGRect(x: 0, y: 0, width: 100, height: 50)
stack.distribution = .fillEqually
let button = UIButton()
button.frame = CGRect(x: 0, y: 0, width: 100, height: 50)
button.addSubview(stack)
view.addSubview(button)
self.view.addSubview(button)
}
Set your button under your controller class like this:
let imageButton: UIButton = {
let b = UIButton(type: .custom)
b.backgroundColor = #colorLiteral(red: 0.3411764801, green: 0.6235294342, blue: 0.1686274558, alpha: 1)
b.layer.cornerRadius = 12
b.clipsToBounds = true
b.translatesAutoresizingMaskIntoConstraints = false
let imageV = UIImageView()
imageV.image = UIImage(named: "yourImage")?.withRenderingMode(.alwaysTemplate)
imageV.tintColor = .white
imageV.contentMode = .scaleAspectFill
imageV.translatesAutoresizingMaskIntoConstraints = false
imageV.widthAnchor.constraint(equalToConstant: 30).isActive = true
let label = UILabel()
label.text = "WhatsApp"
label.textColor = .white
label.font = .systemFont(ofSize: 16, weight: .regular)
let stack = UIStackView(arrangedSubviews: [imageV, label])
stack.distribution = .fill
stack.spacing = 4
stack.axis = .horizontal
stack.translatesAutoresizingMaskIntoConstraints = false
b.addSubview(stack)
stack.heightAnchor.constraint(equalToConstant: 30).isActive = true
stack.widthAnchor.constraint(equalToConstant: 120).isActive = true
stack.centerXAnchor.constraint(equalTo: b.centerXAnchor).isActive = true
stack.centerYAnchor.constraint(equalTo: b.centerYAnchor).isActive = true
return b
}()
Now in viewDidLoad add button and set constraints in your view (in my case on top)
view.addSubview(imageButton)
imageButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
imageButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
imageButton.widthAnchor.constraint(equalToConstant: 200).isActive = true
imageButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
This is the result:

Adding button to view created programatically

I have a view that is created programatically as follows
let viewOne = UIView()
viewOne.contentMode = .scaleAspectFit
viewOne.backgroundColor = UIColor.blue
viewOne.layer.masksToBounds = true
viewOne.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(viewOne)
NSLayoutConstraint.activate([
viewOne.centerXAnchor.constraint(equalTo: view.centerXAnchor),
viewOne.centerYAnchor.constraint(equalTo: view.centerYAnchor),
viewOne.heightAnchor.constraint(equalToConstant:200),
viewOne.widthAnchor.constraint(equalToConstant:200)])
I have the following button that is created programmatically as follows,
let button = UIButton()
button.frame = CGRect(x: 125, y: 125, width: 100, height: 50)
button.backgroundColor = UIColor.green
button.setTitle("Ok", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
viewOne.addSubview(button)
How do I add the button to the center of viewOne. Thanks in advance.
You can try
let viewOne = UIView()
viewOne.contentMode = .scaleAspectFit
viewOne.backgroundColor = UIColor.blue
viewOne.layer.masksToBounds = true
viewOne.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(viewOne)
let button = UIButton()
button.backgroundColor = UIColor.green
button.setTitle("Ok", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
viewOne.addSubview(button)
NSLayoutConstraint.activate([
viewOne.centerXAnchor.constraint(equalTo: view.centerXAnchor),
viewOne.centerYAnchor.constraint(equalTo: view.centerYAnchor),
viewOne.heightAnchor.constraint(equalToConstant:200),
viewOne.widthAnchor.constraint(equalToConstant:200),
button.centerXAnchor.constraint(equalTo: viewOne.centerXAnchor),
button.centerYAnchor.constraint(equalTo: viewOne.centerYAnchor),
button.heightAnchor.constraint(equalToConstant:100),
button.widthAnchor.constraint(equalToConstant:50)
])
Tip:
you can optionally remove these 2 constraints as the button has intrinsic content size by default but if you need a fixed content leave them
button.heightAnchor.constraint(equalToConstant:100),
button.widthAnchor.constraint(equalToConstant:50)
Button it self has the width and height. So you do not have to fix it
button.centerXAnchor.constraint(equalTo: viewOne.centerXAnchor)
button.centerYAnchor.constraint(equalTo: viewOne.centerYAnchor)
You can either use autolayout, as shown below:
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.green
button.setTitle("Ok", for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
viewOne.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: viewOne.centerXAnchor),
button.centerYAnchor.constraint(equalTo: viewOne.centerYAnchor),
button.heightAnchor.constraint(equalToConstant:50),
button.widthAnchor.constraint(equalToConstant:100)])
Or place your button manually, after doing some calculation.
let button = UIButton(frame: CGRect(x: 50, y: 75, width: 100, height: 50))
viewOne.addSubview(button)
I obtained x and y as follows:
x = viewOne width/2 - button width/2 = 200/2 - 100/2 = 50
y = viewOne height/2 - button height/2 = 200/2 - 50/2 = 75

Text and Image inside UIButton

I am stuck in UIButton. I want to add to UIButton text and then next to text add an image, like this:
I want to center it.
when I type this:
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Přidat za: \(priceForCategory)", for: .normal)
button.setImage(UIImage(named: "Coin"), for: .normal)
button.imageEdgeInsets = UIEdgeInsets(top: 6, left: -100, bottom: 6, right: 6)
button.titleLabel?.textAlignment = .right
button.alignImageRight()
button.imageView?.contentMode = .scaleAspectFit
button.backgroundColor = UIColor.custom.orange
button.addTarget(self, action: #selector(handleAddCategory), for: .touchUpInside)
button.tintColor = .white
button.titleLabel?.font = UIFont(name: ".SFUIText-Bold", size: 20)
It shows me:
My height breaks and still it doesn't work.
button.alignImageRight()
Means
func alignImageRight() {
if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
semanticContentAttribute = .forceRightToLeft
}
else {
semanticContentAttribute = .forceLeftToRight
}
}
Thanks for every answer.
Create func for adding UIButton
func addRoundedButton() {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Add for: 100", for: .normal)
button.setImage(UIImage(named: "Coin"), for: .normal)
button.alignImageRight()
//Add this line for rounded corner
button.layer.cornerRadius = 20 // set cornerRadius = height/2
button.backgroundColor = UIColor.orange
button.tintColor = .white
button.titleLabel?.font = UIFont(name: ".SFUIText-Bold", size: 20)
view.addSubview(button)
[button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -15),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.heightAnchor.constraint(equalToConstant: 40),
button.widthAnchor.constraint(equalToConstant: 300)].forEach{ $0.isActive = true }
}
Create extension for UIButton (as you already created). but add UIEdgeInsets in extension.
extension UIButton {
func alignImageRight() {
if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
semanticContentAttribute = .forceRightToLeft
imageEdgeInsets = UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 0)
} else {
semanticContentAttribute = .forceLeftToRight
imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 15)
}
}
}
Output
TIP
For auto adjust width depend on Text and Image, use this extension

Resources