What causes horizontal padding around title of UIButton? - ios

I programmatically created two UIButtons and constrained them to 2 points apart (horizontally). I see extra space, so I clicked the "Debug View Hierachy" button in Xcode, and here is what I see:
What is causing the extra space around the "abc" title? I looked at a few suspect properties of the button and they are all zero. (contentEdgeInsets, titleEdgeInsets)
I haven't done anything to the buttons other than:
let b = UIButton(type: .system)
b.translatesAutoresizingMaskIntoConstraints = false
b.setTitle("...", for: .normal)
The constraints look like:
b1.centerXAnchor.constraint(equalTo: outer.centerXAnchor)
b2.leftAnchor.constraint(equalTo: b1.rightAnchor, constant: 2)
b2.firstBaselineAnchor.constraint(equalTo: b1.firstBaselineAnchor, constant: 0)

UIButton has an intrinsic minimum width of 30-pts.
So, if the title text is shorter than 30-pts, the button's title label will be centered horizontally in the button's 30-pt frame.
You can explicitly set the width to less than 30, but then you'll have to take some other steps to get your button to auto-size with a longer title.

Related

Swift UIStackView utilize full width of UIStackView

In above screen, you can see I am using a UIStackView to fill radio buttons vertically. problem is my radio buttons not utilising the full width of UIStackView when I use stackV.alignment = .leading it shows label as "dis..lified" instead of disqualified.
UISTackView Code
let ratingStackView : UIStackView = {
let stackV = UIStackView()
stackV.translatesAutoresizingMaskIntoConstraints = false
stackV.backgroundColor = UIColor.yellow
stackV.axis = .vertical
stackV.distribution = .fillEqually
stackV.alignment = .leading
return stackV
}()
Layout of UIStackView
func setupView(){
view.addSubview(ratingStackView)
ratingStackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
ratingStackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor,constant: 8).isActive = true
ratingStackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
ratingStackView.heightAnchor.constraint(equalToConstant: 200).isActive = true
//Add radio buttons to stackview
for ratingButton in ratingRadioButtons{
ratingStackView.addArrangedSubview(ratingButton)
}
}
what property I need to set to utilize full width can you please tell I am new to the Swift for radio buttons. I am using DLRadioButton.
To get this working, you need to make following changes in the layout:
1. Set UIStackView's alignment property to fill, i.e.
stackV.alignment = .fill
2. Set UIButton's Horizontal Alignment to left wherever you are creating the RadioButton either in .xib file or through code.
In .xib, you can find the property in interface here:
if you are creating the button using code, use the following line of code:
ratingButton.contentHorizontalAlignment = .left
Let me know if you still face the issue. Happy coding..🙂
Leave alignment with its default value, i.e. .fill – this stretches arranged views in a direction perpendicular to the stack’s axis.
Actually, I suspect that if you are using .leading alignment and do not specify widths of nested controls you are getting auto layout warnings during runtime (could be checked in Visual Debugger in Xcode).
Try proportional distribution.
One more thing to try...Reduce the content hugging priority of the labels.

Stack View, Collapse Space to Content

I have a vertical StackView with three nested Labels
Stack View
PlusMinus (Label)
Currency (Label)
Amount (Label)
The content in the labels are dynamic. I want the stack view to have the 3 items to be next to each other with very minimal spacing in between each other. Such that an example values would read something like "+ $ 45.67", but it seems that all Distribution options under Stack View do not accomplish this. The individual font and color settings under each label are different, so I can not simply use 1 label for these.
Is there anyway to say that each column in a vertical stack view must only take up how much space is taken up by a nested Label.
You need to set the content hugging and the text alignment of the labels appropriately.
So if the whole thing is to be right aligned you do this:
self.signLabel.setContentHuggingPriority(.init(rawValue: 300), for: .horizontal)
self.currencyLabel.setContentHuggingPriority(.init(rawValue: 400), for: .horizontal)
self.valueLabel.setContentHuggingPriority(.init(rawValue: 500), for: .horizontal)
self.signLabel.textAlignment = .right
self.currencyLabel.textAlignment = .right
self.valueLabel.textAlignment = .right
and if it is to be left aligned you do this:
self.signLabel.setContentHuggingPriority(.init(rawValue: 500), for: .horizontal)
self.currencyLabel.setContentHuggingPriority(.init(rawValue: 400), for: .horizontal)
self.valueLabel.setContentHuggingPriority(.init(rawValue: 300), for: .horizontal)
self.signLabel.textAlignment = .left
self.currencyLabel.textAlignment = .left
self.valueLabel.textAlignment = .left
(you can vary the numbers as long as they stay in the increasing order).
What this does is to give the layout system clues as to which of the UILabels should compress down to hug it's content first.
This should work with a UIStackView set to horizontal and using the default settings.

Why do my objects get placed and sized so perfectly when I add them to a UIKitView?

So I have the following UIKit elements I am adding to my UIViewController to control the simulation that appears. I expected to have to write a lot of placement code but instead everything appears perfectly no-mater what device... my question is why?
let menuButton = UIButton()
let statusLabel = UILabel()
let segmentedLabel = UISegmentedControl(items: ["None", "Glow", "Cloud"])
func initializeUI() {
//The menu button that opens up the options for the simulations
menuButton.layer.borderColor = UIColor.lightGray.cgColor
menuButton.layer.borderWidth = 1
menuButton.layer.cornerRadius = 5
menuButton.layer.backgroundColor = UIColor.darkGray.cgColor
menuButton.showsTouchWhenHighlighted = true
menuButton.imageView?.contentMode = UIViewContentMode.scaleAspectFit
menuButton.setImage(UIImage(named: "hamburger.png"), for: UIControlState.normal)
menuButton.addTarget(self, action: #selector(menuPress), for: UIControlEvents.touchDown)
view.addSubview(menuButton)
//Will display the status of the simulation
statusLabel.text = "Particle Simulation"
statusLabel.textColor = UIColor.darkGray
view.addSubview(statusLabel)
//Will display visual options
view.addSubview(segmentedLabel)
}
The size of each of the elements is perfect and they dont overlap. The label is in the bottom left corner the button is in the bottom right and the segmented view is in the top left corner (of my landscape app).
An additional question I have is if I wanted to start placing these objects programmatically how would I do so? The elements don't have an attribute position that I can work with and if I do something like menuButton.frame.size.height *= 20 that does not make the menu button super tall.
The reason this works is that all of your UI items in your example have an “intrinsic” content size so they are able to create their own frame. Add a standard UIView the same way and you will be out of luck. Also the views are creating their own constraints based on initial frames using Auto Resizing Masks(Also known as Springs and Struts).
Finally to place items you would set the frame directly. Just math. Good luck.
Super important to understand intrinsic content size. Some frame changes might require you to turn off automatic constraints but this is usually not the case but could be the problem with your button
UIView.translatesAutoresizingMaskIntoConstraints = false

Two labels alignment and its constraints

I have 2 labels: the description label (w/ red background) and the results label (gray text)
How do i set constraints for this example in order to have the results label with the size of its content and the description label until the results leadingAnchor? (like i have in the second row)
Objective C
[self.customTextLabel.trailingAnchor constraintLessThanOrEqualToAnchor:self.counterLabel.leadingAnchor].active = YES;
[self.counterLabel.widthAnchor constraintGreaterThanOrEqualToConstant:0].active = YES;
swift
titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: counterLabel.leadingAnchor).isActive = true
counterLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
I have a solution that i think it's ugly.
self.counterLabelWidthConstraint = [self.counterLabel.widthAnchor constraintEqualToConstant:0];
self.counterLabelWidthConstraint.active = YES;
And then after i set the text:
self.counterLabelWidthConstraint.constant = [self.counterLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, self.counterLabel.height)].width;
The way to do this with auto layout is by using the contentCompressionResistancePriority of the 2 labels. Set the pririty to NSLayoutPriorityRequired for the second label and something lower like NSLayoutPriorityDefaultLow for the first label. Then, as long as the 2 labels have proper constraints anchoring them to their superview and each other, the first label should compress while the second label should not.
You just need to increase the horizontal compression resistance of the right/gray label to be higher than that of the left/red label. This tells the visual layout that, in the event that there is not enough space for both labels, the one on the left will be compressed before shrinking the label on the right. 750 is the default for all views, so just increase the right/gray label's horizontal compression resistance to 751 and you should be good to go.
Swift 5 programatically:
<#label#>.setContentCompressionResistancePriority(.required, for: .horizontal)
Labels with this property will not compress horizontally.
You can set constraints for in storyboard itself. Select Label 1 (red back ground) and label's superview set widths are equal constraint. Select Label 1 and double tap on its width constraint, from the resultant window, you could see Lable 1 width equal to superview with value constant as '1'. change '1' to 0.7 or whichever the percentage you want.

Button height & programmical setTitle & Autolayout

I use autolayout.
On a screen I have one button + one imageview.
Between this element I set vertical space = 10
| button |
|
10
|
| image |
If I setTitle of button with long text button overlap on image.
How to fix this problem?
EDIT:
Left image as it look now - right as I want that it to be:
This is my constraints:
This is my code:
btnFullLic2.setTitle(getLoc("FULL_VER_DESC"), forState: UIControlState.Normal)
btnFullLic2.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
btnFullLic2.titleLabel?.numberOfLines = 0
btnFullLic2.titleLabel?.clipsToBounds = true
You have to either give maximum height to button or minimum height to image or both depending on your requirement. Adding maximum height constraint to button, will keep the button height from not going beyond that value. While giving minimum height to image, it will have minimum height and overlap the button if it increases.
Hope you understood the concept and get desired result..

Resources