Adding buttons programmatically to stackView in Swift - ios

I've tried to add buttons dynamically/programmatically in UIStackView that I've built with interface builder but they failed to show up when I run the application. The number of buttons that's supposed to be added ranging normally from 4-6. Can you guys tell me what's wrong with the code

#Nowonder I just recreate what you are trying to achieve. The following are the steps.
Add a UIStackView in viewController from Interface Builder and add required constraints.
Add the following code.
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
button.setTitle("btn 1", for: .normal)
button.backgroundColor = UIColor.red
button.translatesAutoresizingMaskIntoConstraints = false
let button2 = UIButton()
button2.setTitle("btn 2", for: .normal)
button2.backgroundColor = UIColor.gray
button2.translatesAutoresizingMaskIntoConstraints = false
let button3 = UIButton()
button3.setTitle("btn 3", for: .normal)
button3.backgroundColor = UIColor.brown
button3.translatesAutoresizingMaskIntoConstraints = false
buttonStackView.alignment = .fill
buttonStackView.distribution = .fillEqually
buttonStackView.spacing = 8.0
buttonStackView.addArrangedSubview(button)
buttonStackView.addArrangedSubview(button2)
buttonStackView.addArrangedSubview(button3)
}
Following is the outcome.
Hope it helps.

I think you need to do few more steps before the button will start showing up.
Add the stack view to current view's subviews
view.addSubview(buttonStackView)
Now each of these views buttons, as well as the stackView, needs to set the translatesAutoresizingMaskIntoConstraints to false.
button1.translatesAutoresizingMaskIntoConstraints = false
button2.translatesAutoresizingMaskIntoConstraints = false
buttonStackView.translatesAutoresizingMaskIntoConstraints = false
Now set the stackView contraints
NSLayoutConstraint.activate([
buttonStackView.topAnchor.constraint(equalTo: view.topAnchor),
buttonStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
buttonStackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
buttonStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor)])
You will need to provide the constraints, or can override the intrinsic size of stackView.
override func intrinsicContentSize() -> CGSize
{
return CGSizeMake(200, 40)
}
As UILabel have the intrinsic size and so, the button will show, if not you will have to set the constraints for them also to be safe.

SOLVED!
The button showed up when I set the type of the button with the instance method init(type:)

Related

Buttons not working when running app on Xcode 12 [duplicate]

This question already has answers here:
Since Updating to xcode 12 I am not able to place any UIControl inside UITableViewCell
(3 answers)
Closed 2 years ago.
I'm using Version 12.0.1 (12A7300). I have some buttons on UIViews and others in UITableViewCells. The actions for the buttons inside table cells are in the parent view controller which I access using protocols. When running the app either on simulators or on my device. All the buttons are added programmatically. The buttons do not work. I reinstalled Xcode Version 11.7 (11E801a) and everything works.
Any way to get around this issue? Is this a bug or did something change and I'm not aware of?
Here is an example of my code. In the UITableViewCell:
var addToCartDelegate: AddToCartDelegate?
let addToCartButton: UIButton = {
let button = UIButton()
return button
}()
let applePayButton: PKPaymentButton = {
let button = PKPaymentButton(paymentButtonType: .buy, paymentButtonStyle: .whiteOutline)
return button
}()
func configureAddToCartButton() {
let button = addToCartButton
button.setTitle("Add To Cart", for: .normal)
button.titleLabel?.font = getScaledFont(forFont: Fonts.bold, textStyle: .body)
button.setTitleColor(Colors.primaryButtonTintColor, for: .normal)
button.tintColor = Colors.primaryButtonTintColor
button.backgroundColor = Colors.primaryButtonBackgroundColor
button.layer.cornerRadius = Attributes.cornerRadius
button.addTarget(self, action: #selector(addToCartButtonWasTapped), for: .touchUpInside)
}
func configureStackView() {
var stackView = UIStackView()
if useApplePay {
stackView = UIStackView(arrangedSubviews: [addToCartButton, applePayButton])
} else {
stackView = UIStackView(arrangedSubviews: [addToCartButton])
}
stackView.axis = .horizontal
stackView.spacing = 16
stackView.distribution = .fillEqually
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 44).isActive = true
stackView.rightAnchor.constraint(equalTo: rightAnchor, constant: -16).isActive = true
stackView.leftAnchor.constraint(equalTo: leftAnchor, constant: 16).isActive = true
}
#objc func addToCartButtonWasTapped() {
addToCartDelegate?.addItemToCart()
}
#objc func applePayButtonWasTapped() {
addToCartDelegate?.buyWithApplePay()
}
As bluntly as possible you should not add content to a cell's view directly. Instead, content should be added to the cell's contentView by using contentView.addSubview(). In iOS 14, a cell's contentView appears above content added directly to the cell. This will in effect prohibit a user from interacting with those items, as the contentView will absorb all interaction. See the below images:
Pre iOS 14, notice the contentView behind all views added to the cell
iOS 14 and above, notice the contentView is in front of all views added directly to the cell
Do the buttons visible?
Make sure you used configureEditProfileButton() function and added "editProfileButton" to main view.

Swift - UIButton setting constraints programmatically

I am having problems with setting my height and width constraints for my UIButton. I have no idea why this code is not working:
let wishButton: UIButton = {
let v = UIButton()
v.setImage(UIImage(named: "wishButton"), for: .normal)
v.translatesAutoresizingMaskIntoConstraints = false
v.addTarget(self, action: #selector(wishButtonTapped), for: .touchUpInside)
return v
}()
These are my constraints:
wishButton.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
wishButton.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: 150).isActive = true
wishButton.heightAnchor.constraint(equalToConstant: 100).isActive = true
wishButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
This is how it looks at the moment:
It's weird because the other two constraints are working just fine. Probably just a stupid mistake but I am quite new and grateful for every help :)
Constraints have no issues. The issue is with the image. Your image is not big enough to fill the frame. You can set the .setBackgroundImage of the button to make it automatically scalable.
v.setBackgroundImage(UIImage(named: "wishButton"), for: .normal)
or you can make the content Alignment equal to .fill on both directions.
One thing I noticed is it doesn't look like you have set the frame of the button.
Try adding this line after you initialize the button:
v.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
I'm not sure if it is required but if your button isn't showing up at all, this may fix the issue
Add this to your UIButton initialisation, it will work
v.contentVerticalAlignment = .fill
v.contentHorizontalAlignment = .fill
It will scale your image.
let wishButton: UIButton = {
let v = UIButton()
v.setImage(UIImage(named: "wishButton"), for: .normal)
v.translatesAutoresizingMaskIntoConstraints = false
v.contentVerticalAlignment = .fill
v.contentHorizontalAlignment = .fill
v.addTarget(self, action: #selector(wishButtonTapped), for: .touchUpInside)
return v
}()

Programmatic UIButton as a subview of UIView is not responding

I'm making a class that extends UITableViewCell--it's going to display a social media newsfeed, with each cell being someone's post.
I've added a UIButton as a subview of a UIView, So the UIView will contain both the button and a label as subviews, like this. (Sorry the image is kind of small but I think you'll get the idea of it).
However, the UIButton isn't responding to taps, not even highlighted. Its superview itself has a number of superviews, all stacks, but I checked and every superview's isUserInteractionEnabled is true.
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button(UIImage(named: "vertical_dots")?.withRenderingMode(.alwaysOriginal), for: .normal)
button.backgroundColor = .white
button.imageView?.contentMode = .scaleAspectFit
button.addTarget(self, action: #selector(myButtonAction), for: .touchUpInside)
label.text = "\(userPost?.minutesAgo)m"
label.font = label.font.withSize(11.0)
label.textColor = .lightGray
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 1
let superview = UIView()
superview.translatesAutoresizingMaskIntoConstraints = false
superview.addSubview(button)
superview.addSubview(label)
label.leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
label.heightAnchor.constraint(equalToConstant: 40).isActive = true
label.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true
button.leftAnchor.constraint(equalTo: label.rightAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 20).isActive = true
button.widthAnchor.constraint(equalToConstant: 15).isActive = true
I'm getting a lot of constraint warning but I don't think that would affect things?
Note that this is only a chunk of my code, and I am adding the superview to other views, but I'm not showing all that here. None of my views have frames, only constraints, and they're all appearing correctly.
I don't think this question has been asked before because others are working with Interface Builder, while I'm doing it all programmatically.
Based on the constraints you've shown, you cannot tap your button because it is outside the bounds of your superview.
To confirm this, add this line:
superview.clipsToBounds = true
and you almost certainly won't even see your button or label.
You need to either give superview width and height constraints, or you need to add constraints so the subview(s) determine the superview's bounds.
For example, add these lines:
label.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
button.rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true
Could be that the table view cell thinks it is being selected and is cancelling the touch of the button.
Try this:
tableView.allowsSelection = false
If that's not the case, try checking the frame of your button to see if you're actually tapping on it. You can use the Debug View Hierarchy button to check this.
Furthermore, you can try:
button.clipSubviews = true
If you can't see your button, then the frame is wrong, and you can't tap outside of the frame of a button. (Even if you can see it.)

Swift 3 - Programmatically build a button with an image and label

I am currently trying to build an interface (programmatically) with 5 x buttons, containing an image and a label.
I have done this successfully for ONE button using a UIStackView (holding the UIButton and a UIlabel).
I have two questions for this forum…
A UIButton can be built to display a title OR an image, can it have both?
Can a ‘for in’ loop be used to generate 5 x individual buttons? i.e: a way to re-use code instead of typing out code for 5 x buttons, 5 x labels, 5 x stack views.
My working UIStackView button code is as follows:
// Button
let btnSettings = UIButton()
// btnSettings.setTitle("Settings", for: .normal)
btnSettings.setImage(#imageLiteral(resourceName: "star-in-circle"), for: .normal)
btnSettings.heightAnchor.constraint(equalToConstant: 100.0).isActive = true
btnSettings.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
btnSettings.contentMode = .scaleAspectFit
btnSettings.addTarget(self, action: #selector(openSettings), for: .touchUpInside)
btnSettings.translatesAutoresizingMaskIntoConstraints = false
// Text Label
let textLabel = UILabel()
textLabel.backgroundColor = UIColor.clear
textLabel.widthAnchor.constraint(equalToConstant: 100.0).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
textLabel.font = UIFont.boldSystemFont(ofSize: 18)
textLabel.text = "Settings"
textLabel.textAlignment = .center
// Stack View
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.vertical
stackView.distribution = UIStackViewDistribution.equalSpacing
stackView.alignment = UIStackViewAlignment.center
stackView.spacing = 1.0
stackView.addArrangedSubview(btnSettings)
stackView.addArrangedSubview(textLabel)
stackView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackView)
// Constraints
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
Update / Solved Question 1
I needed to use the button.setBackgroundImage() in order for the button title to show up with the image.
btnSettings.setBackgroundImage(#imageLiteral(resourceName: "star-in-circle"), for: .normal)
btnSettings.setTitle("Button Title", for: .normal)
btnSettings.backgroundColor = UIColor.white
Of course, UIButton can have both an image or/and text. You can use:
button.setImage(image: UIImage?, for: UIControlState)
button.setTitle(title: String?, for: UIControlState)
Create a function that will return UIButton and do something like that:
let button = generateButton()
stackView.addArrangedSubview(button)

define UIButton programmatically swift

I'm trying to add a button programmatically as follows.
lazy var myButton: UIButton = {
let button = UIButton(type: .System)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = UIColor.blueColor()
button.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2)
button.addTarget(self, action: "buttonAction", forControlEvents: .TouchUpInside)
return button
}()
My intention was to create a button with view's width and height, half of view's height. And I also have LeftAnchor, RightAnchor, WidthAnchor, TopAnchor constraints to this button.
I have the following piece of code in my viewDidLoad()
view.addSubview(myButton)
when I try to run this code, I'm not able to see exactly what i want to have on my simulator.
I would like to see button width = view' width and height of button = view height /2
instead I see small button on the left top of the simulator. How do i resolve this issue?
Thanks !
A better bet would be to use AutoLayoutConstraints.
var myButtonHeight: NSLayoutConstraint
view.addSubview(myButton)
myButton.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
myButtonHeight = myButton.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.5)
myButtonHeight.isActive = true
It's not clear from your comment if changing of the button height is onTouchDown, of if its a toggle as a result of onTouchUpInside, but either way
myButtonHeight.constant = (buttonShouldBeTall) ? 20 : 0
view.setNeedsLayout()
Please keep in mind you'll need to position the leading/trailing/centerX anchor and leading/trailing/centerY anchor as well, depending on where you need it to be

Resources