Swift Button is not clickable on whole area - ios

I have a problem with a UIButton. This is how I create it:
let backButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.setImage(UIImage(named: "backButton"), for: .normal)
v.backgroundColor = .cyan
v.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
return v
}()
Constraints:
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true
backButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
backButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
backButton.widthAnchor.constraint(equalToConstant: 40).isActive = true
And this is how it looks:
I create the button with the extra area on purpose for a better UX.
Problem:
The button is only clickable at the left half of the button. Why is this happening? It should also be clickable on the invisible area (.cyan for a better understanding).
What am I missing here?

Related

2 UIButton with dynamic text and font size

I am using 2 UIButton in same y position. The one on the left is shorter in text than the one on the right:
example image
I am adding
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.minimumScaleFactor = 0.5
for each button. The problem is that one button font size get smaller than the other. For example, they are both with font size 18, one might get 12 and the other will stay 18. I want them both to be 15 (the larger possible keeping both texts visible)
The text inserted to the buttons changes so i can't use aspect ratio.
Also, i don't want to use the font of the button with the smaller text because the difference is sometimes to high.
You can do it programmatically with stack view, declare your buttons under your controller class:
let myButton: UIButton = {
let b = UIButton()
b.backgroundColor = .white
b.setTitle("Short button", for: .normal)
b.setTitleColor(.black, for: .normal)
b.titleLabel?.font = .systemFont(ofSize: 17, weight: .regular)
b.layer.cornerRadius = 12
b.clipsToBounds = true
b.titleLabel?.adjustsFontSizeToFitWidth = true
b.titleLabel?.minimumScaleFactor = 0.5
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
let myButton2: UIButton = {
let b = UIButton()
b.backgroundColor = .white
b.setTitle("Much Much Much longer button", for: .normal)
b.setTitleColor(.black, for: .normal)
b.titleLabel?.font = .systemFont(ofSize: 17, weight: .regular)
b.layer.cornerRadius = 12
b.clipsToBounds = true
b.titleLabel?.adjustsFontSizeToFitWidth = true
b.titleLabel?.minimumScaleFactor = 0.5
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
Now in viewDidLoad set stackView and constraints (I set 6 for space, but you can set your prefer space, look at the comment to do it):
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .darkGray
let stackView = UIStackView(arrangedSubviews: [myButton, myButton2])
stackView.distribution = .fillProportionally
stackView.spacing = 6 // change space betwenn buttons
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
This is the result shortButton/shortButton and shortButton/longerButton, the width of the buttons changes dynamically according to the text:

Swift button from nested UIView is not clickable

I have a ViewController and another UIView called MySubview which contains a simple button.
If I add MySubview into ViewController the button is unclickable. However if I put the button directly inside ViewController everything works as expected.
Example:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let button = MySubview()
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
}
}
class MySubview: UIView {
let button: UIButton = {
let button = UIButton()
button.setTitle("MyButton", for: .normal)
button.setTitleColor(.label, for: .normal)
button.addTarget(self, action: #selector(myfunc), for: .touchUpInside)
return button
}()
#objc func myfunc() {
print("clicked")
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: topAnchor, constant: 0).isActive = true
button.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
When I click on the button I do not see printed message.
Your code is confused, if I understand well take a look to my code below, declare your button and your view under your controller class:
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton()
button.setTitle("MyButton", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.addTarget(self, action: #selector(myfunc), for: .touchUpInside)
return button
}()
let mysubview = UIView()
...
now in viewDidLoad set constraints like this:
override func viewDidLoad() {
super.viewDidLoad()
mysubview.backgroundColor = .red
view.addSubview(mysubview)
mysubview.translatesAutoresizingMaskIntoConstraints = false
mysubview.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
mysubview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
mysubview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
mysubview.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
mysubview.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: mysubview.topAnchor, constant: 20).isActive = true
button.leadingAnchor.constraint(equalTo: mysubview.leadingAnchor, constant: 20).isActive = true
button.trailingAnchor.constraint(equalTo: mysubview.trailingAnchor, constant: -20).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
now add your func
#objc func myfunc() {
print("clicked")
}
complete code:
import UIKit
class ViewController: UIViewController {
let button: UIButton = {
let button = UIButton()
button.setTitle("MyButton", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .black
button.addTarget(self, action: #selector(myfunc), for: .touchUpInside)
return button
}()
let mysubview = UIView()
override func viewDidLoad() {
super.viewDidLoad()
mysubview.backgroundColor = .red
view.addSubview(mysubview)
mysubview.translatesAutoresizingMaskIntoConstraints = false
mysubview.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
mysubview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
mysubview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
mysubview.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
mysubview.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.topAnchor.constraint(equalTo: mysubview.topAnchor, constant: 20).isActive = true
button.leadingAnchor.constraint(equalTo: mysubview.leadingAnchor, constant: 20).isActive = true
button.trailingAnchor.constraint(equalTo: mysubview.trailingAnchor, constant: -20).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
#objc func myfunc() {
print("clicked")
}
}
If your subview has a frame of size zero, you won't be able to tap the button even if you can see it. Using a view debugger from Xcode will show you if this is the issue.
Also, check if the subview has user interaction enabled when adding it.
I think , the problem is in layers (I mean, subviews). Try this instead of view.addSubview(button) :
view.insertSubview(your_button_view, at: view.subviews.count)

Set contentMode for Image inside Button in Swift

I am trying to setImage inside my UIButton but the image appears smaller inside the button although there is "free-space" and I also set the contentMode.
Button:
let noteButton: UIButton = {
let v = UIButton()
v.setImage(UIImage(systemName: "pencil"), for: .normal)
v.tintColor = UIColor.white
v.imageView?.contentMode = .scaleAspectFit
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
Constraints:
noteButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
noteButton.widthAnchor.constraint(equalToConstant: 30).isActive = true
noteButton.centerYAnchor.constraint(equalTo: itemView.centerYAnchor).isActive = true
noteButton.leadingAnchor.constraint(equalTo: linkButton.leadingAnchor, constant: 50).isActive = true
Result:
I fixed the issue. I just had to add these to lines:
v.contentHorizontalAlignment = .fill
v.contentVerticalAlignment = .fill

Setting background color for customView has no effect

I have a customView which I display by after a button-tap. My problem is that setting .backgroundColor has no effect and it is just a clear background.
CustomView:
let wishWishView: WishStackView = {
let v = WishStackView()
v.backgroundColor = .cyan
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
AddViewButton:
#objc func addWishButtonTapped(){
self.view.addSubview(self.wishWishView)
wishWishView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
wishWishView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
}
WishWishView is just a simple UIView with a StackView inside of it.
My guess would be that it is a UIStackView, which does not support having a background color.
You can read more about why this happens, and possible workarounds here: https://stackoverflow.com/a/34868367/3992237
Your code is Totally wrong, first you set your view like this:
let wishWishView: UIView = {
let v = UIView()
v.backgroundColor = .cyan
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
now set your stackView, in my example I put in 2 label in vertical axis:
let stackView: UIStackView = {
let label1 = UILabel()
label1.text = "Label 1"
label1.textColor = .red
label1.font = UIFont.systemFont(ofSize: 16)
let label2 = UILabel()
label2.text = "Label 2"
label2.textColor = .black
label2.font = UIFont.systemFont(ofSize: 16)
let sV = UIStackView(arrangedSubviews: [label1, label2])
sV.axis = .vertical
sV.distribution = .fillEqually
sV.translatesAutoresizingMaskIntoConstraints = false
return sV
}()
after that set your button:
let buttonAddView: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Add View", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .red
button.addTarget(self, action: #selector(addWishButtonTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
in viewDidLoad add button and set constraints
view.addSubview(buttonAddView)
buttonAddView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20).isActive = true
buttonAddView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20).isActive = true
buttonAddView.heightAnchor.constraint(equalToConstant: 50).isActive = true
buttonAddView.widthAnchor.constraint(equalToConstant: 120).isActive = true
now write the function that add the view with stack view inside when the button is tapped with right constraints, in your function you call only the position of the view but not the size of it. Write your function like this:
#objc func addWishButtonTapped(){
view.addSubview(self.wishWishView)
wishWishView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
wishWishView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
wishWishView.heightAnchor.constraint(equalToConstant: 100).isActive = true
wishWishView.widthAnchor.constraint(equalToConstant: 200).isActive = true
wishWishView.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: wishWishView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: wishWishView.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: wishWishView.leadingAnchor, constant: 10).isActive = true
stackView.trailingAnchor.constraint(equalTo: wishWishView.trailingAnchor, constant: -10).isActive = true
}
Now simply change background of the wishWishView to automatically set stackView background, and that's it...

How do you vertically align the titles of two different buttons with different title sizes?

The follow button next to the user's name seems to be vertically centered whereas I want to align the baselines of both titles.
I'm looking to align this programmatically.
suppose you don't have fixed width and height both of your label and button.
Then your code will look like this.Just to mention i am using LayoutAnchor as it's easy.
func autolayoutTitle() {
let label = UILabel()
label.backgroundColor = UIColor.red
label.textColor = UIColor.white
label.text = "Nafeez Zawahir"
label.textAlignment = .right
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
//label.widthAnchor.constraint(equalToConstant: 150).isActive = true
// label.heightAnchor.constraint(equalToConstant: 40).isActive = true
label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 40).isActive = true
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
let button = UIButton()
//button.titleLabel?.text = "Follow"
button.setTitle("Follow", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.backgroundColor = UIColor.darkGray
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
// button.widthAnchor.constraint(equalToConstant: 100).isActive = true
// button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.leftAnchor.constraint(equalTo: label.rightAnchor, constant: 2).isActive = true
button.lastBaselineAnchor.constraint(equalTo: label.lastBaselineAnchor).isActive = true
// button.lastBaselineAnchor.constraint(equalTo: label.lastBaselineAnchor, constant: -10).isActive = true
}
And the output is:
But if you have fixed width and height of your label as well as your button too, then you have to tune your button's baseline anchoring with label's baseline anchor which depends on label's height.
Let's say you have label's height 40 like this.
label.heightAnchor.constraint(equalToConstant: 40).isActive = true
Then your button's baseline anchoring should be like this.
button.lastBaselineAnchor.constraint(equalTo: label.lastBaselineAnchor, constant: -10).isActive = true
Below is the code:
func autolayoutTitle() {
let label = UILabel()
label.backgroundColor = UIColor.red
label.textColor = UIColor.white
label.text = "Nafeez Zawahir"
label.textAlignment = .right
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
label.widthAnchor.constraint(equalToConstant: 150).isActive = true
label.heightAnchor.constraint(equalToConstant: 40).isActive = true
label.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 40).isActive = true
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
let button = UIButton()
//button.titleLabel?.text = "Follow"
button.setTitle("Follow", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.backgroundColor = UIColor.darkGray
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.leftAnchor.constraint(equalTo: label.rightAnchor, constant: 2).isActive = true
// button.lastBaselineAnchor.constraint(equalTo: label.lastBaselineAnchor).isActive = true
button.lastBaselineAnchor.constraint(equalTo: label.lastBaselineAnchor, constant: -10).isActive = true
}
And this is the output:

Resources