let btn = UIButton(frame: CGRect.zero)
btn.setImage(..., for: UIControlState.normal)
addSubview(btn)
btn.translatesAutoresizingMaskIntoConstraints = false
btn.rightAnchor.constraint(equalTo: btn.superview!.rightAnchor, constant: -32).isActive = true
btn.topAnchor.constraint(equalTo: btn.superview!.topAnchor, constant: 6 + vc.view.safeAreaInsets.top).isActive = true
btn.widthAnchor.constraint(equalToConstant: 32)
btn.heightAnchor.constraint(equalToConstant: 32)
btn.backgroundColor = .black //to check the rect
The size of the image is 16x16. The button should be larger at least twice (because of small touch area).
But when I run the app the button becomes 16x22 (22 - height). In "debug view hierarchy" I also see that its constraints become width==16 and height==22.
In the same time distance constraints work normally.
So what is my mistake? Or must I use image resources which fit buttons 1:1?
Try this. It tells the button that it shouldn't hug its contents, and spread out according to its other constraints:
btn.setContentHuggingPriority(.defaultLow, for: .horizontal)
btn.setContentHuggingPriority(.defaultLow, for: .vertical)
Related
I'm working on an custom tab bar button stuff and want to know how can I move up the button using the view's height or something programatically.
I have this plus button and I want to move up 10pt from the bottom.
let newButton = UIButton(frame: CGRect(x: 0, y: 0, width: 65, height: 65))
newButton.imageView?.contentMode = .scaleAspectFit
newButton.contentHorizontalAlignment = .fill
newButton.contentVerticalAlignment = .fill
var newButtonFrame = newEventButton.frame
newButtonFrame.origin.y = view.bounds.height - 65 - 10
So the code above works when an iPhone doesn't have a home button notch. Like get the height of the view and subtract button height and 10.
But, obviously, when there is a notch, it becomes something like the image below.
I want to move up the button from the bottom of the tabbar, so how can I get the height of the screen till the bottom of the tabbar, which works on both with/without home notch?
While #wonder is completly correct in his answer, there are still some flaws.
First you shouldn't use frame to determine views layout like this.
Second there are no need to reach into the UIApplication singleton of windows for this. As it's located in the parents view. Either in safeAreaInsets or safeAreaLayoutGuide.
The layout guide is for anchor points, which is way more sustainable than frames.
Here you have example code of how it can look:
let newButton = UIButton()
newButton.imageView?.contentMode = .scaleAspectFit
newButton.contentHorizontalAlignment = .fill
newButton.contentVerticalAlignment = .fill
NSLayoutConstraint.activate([
newButton.heightAnchor.constraint(equalTo: 65),
newButton.widthAnchor.constraint(equalTo: 65),
newButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
newButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10)
])
For button origin use safe are insets, ıf device has notch safeAreaBottom will be like 44 , if not it will be 0:
let safeAreaBottom = UIApplication.shared.windows.first?.safeAreaInsets.bottom
Then use it :
newButtonFrame.origin.y = view.bounds.height - safeAreaBottom - 65 - 10
#objc func buttonRoundPlayer(){
buttonRound.setTitle("PlayerControl", for: .normal)
buttonRound.addTarget(self, action: #selector(roundhandle), for: .touchUpInside)
buttonRound.backgroundColor = .clear
buttonRound.layer.cornerRadius = 5
buttonRound.layer.borderWidth = 1
buttonRound.layer.borderColor = UIColor.white.cgColor
buttonRound.frame = CGRect(x: self.frame.width + 260, y: 260, width: 50, height: 50)
self.view!.addSubview(buttonRound)
}
The button doesn't appear on the scene unless self.view!.addSubview(buttonRound) and CGRect doesn't seem to affect the button's position and the button appears on the top left hand corner and no changes seems to affect the button position.
I was hoping to get some help in this issue.
Thank you in advance.
If you call this function in viewDidLoad() then self.frame may be just an empty rect. Frame calculations are best done in viewWillAppear() or viewWillLayoutSubviews().
But in general, it’s better to use auto layout. For example:
buttonRound.translatesAutoresizingMaskIntoConstraints = false
buttonRound.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
buttonRound.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
For more info the Apple's Auto Layout Guide or search for the many tutorials available.
I have one view whose height is 50
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
Now what I want to do is when I click button (buton1), I want to change its height to 20. So I make below and it's working fine.
myView.heightAnchor.constraint(equalToConstant: 20).isActive = true
Now in myView I have another button (button2), when I click on it, I want to go back to previous height of 50, but it's not working.
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
Problem is here. Now the view is not re-sizing like when click on button1.
Note :
When I click on button1 & use below code, re-size also not work.
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
What I found is if I try to re-size to higher size of current size, it don't work.
Any idea why view is not re-sizing?
Edit 1
Even I wrote below after updating height anchor but still not working.
myView.layoutIfNeeded()
self.view.layoutIfNeeded()
Edit 2
Working Option 1
When click on button1, I do below and works.
myView.heightAnchor.constraint(equalToConstant: 40).isActive = true
When click on button2, I do below and works.
myView.heightAnchor.constraint(equalToConstant: 30).isActive = true
Not Working Option 2
When click on button1, I do below and works.
myView.heightAnchor.constraint(equalToConstant: 40).isActive = true
When click on button2, I do below and NOT WORK.
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
So what I found is I change height higher then previous, it don't work
At the moment, you are creating a new rule about the view's height every time. iOS doesn't understand how to make a view both 10, 20 and 30 pixels high, so it will do the lowest one. To avoid creating conflicting heights, you need to have one height rule and change that one's constant when needed. Here's a somewhat contained example.
class CoolView: UIViewController {
lazy var myView = UIView()
lazy var myViewHeightAnchor = myView.heightAnchor.constraint(equalToConstant: 10)
override func viewDidLoad() {
super.viewDidLoad()
myView.backgroundColor = .black//livesMatters
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
NSLayoutConstraint.activate([
myView.topAnchor.constraint(equalTo: view.topAnchor),
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
myView.widthAnchor.constraint(equalToConstant: 10),
myViewHeightAnchor
])
}
#objc func someButtonTapped() {
myViewHeightAnchor.constant = 20
view.layoutIfNeeded()
}
#objc func anotherButtonTapped() {
myViewHeightAnchor.constant = 30
view.layoutIfNeeded()
}
}
You should keep the reference to the constraint and change the constant value and then call layoutIfNeeded method to achieve this. Here's how:
// First
let heightConstraint = myView.heightAnchor.constraint(equalToConstant: 50)
heightConstraint.isActive = true
// Then to change
heightConstraint.constant = 20
myView.layoutIfNeeded()
As requested I'm adding playground sample here (try this out on Xcode-playground):
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
var heightConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
let sampleView = UIView()
sampleView.backgroundColor = .green
sampleView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(sampleView)
heightConstraint = sampleView.heightAnchor.constraint(equalToConstant: 50)
let button = UIButton(type: .contactAdd)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(handleAction), for: .touchUpInside)
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
sampleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
sampleView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
heightConstraint!,
sampleView.widthAnchor.constraint(equalToConstant: 200)
])
}
#objc func handleAction() {
heightConstraint?.constant = heightConstraint?.constant == 50 ? 30 : 50
}
}
PlaygroundPage.current.setLiveView(ViewController())
Here you can see how updating the heightConstraint changes the height of the sampleView when the button is clicked. I'm toggling between 50 and 30 points in height here.
So short story,
I was adding below line every time I want to update height which is wrong.
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
With this, what it do is take the least height and set that. Ex If I set height 3 times as 100, 20, 80, it will take 20 as the height even I run with 80.
To avoid this, we have to add a variable for the height constraint
var heightConstraint : NSLayoutConstraint?
Now while adding constraints to view, add as below.
heightConstraint = mainCVView.heightAnchor.constraint(equalToConstant: 50)
heightConstraint!.isActive = true
Now whenever you want to update height do it as below instead of adding constraint again.
heightConstraint?.constant = 0
heightConstraint?.isActive = true
myView.layoutIfNeeded()
This is most important step and not to add constraint the way I was adding in question
So whenever you want to update just update constant as show above. That's it.
I want to add UIButton like this:
let switchTheme: UIButton = {
let button = UIButton.init()
button.backgroundColor = .red
button.setTitleColor(.blue, for: .normal)
button.setTitle(Settings.isLightTheme() ? Strings.Various.switchToDark.value : Strings.Various.switchToLight.value, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
And then set constraints like:
switchTheme.bottomAnchor.constraint(equalTo: view.bottomAnchor)
switchTheme.leftAnchor.constraint(equalTo: view.leftAnchor)
switchTheme.rightAnchor.constraint(equalTo: view.rightAnchor)
switchTheme.heightAnchor.constraint(equalToConstant: 40.0)
But it shown not on bottom as it suppose to but on top and without constraints applied.
You will need to set constraints activate state = true. You can do it simply,
NSLayoutConstraint.activate([
//Move your existing code HERE with comma separated
])
In case of any problem, you can check this following function:
func setConstraints() {
NSLayoutConstraint.activate([
switchTheme.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), // bottomAnchor to set bottom target.
switchTheme.leftAnchor.constraint(equalTo: self.view.leftAnchor), // leftAnchor to set X left
switchTheme.rightAnchor.constraint(equalTo: self.view.rightAnchor), // rightAnchor to set X right
switchTheme.heightAnchor.constraint(equalToConstant: 40.0) //heightAnchor to set appropriate height.
])
}
You need to active those constraints just simple like this :
switchTheme.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
switchTheme.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
switchTheme.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
switchTheme.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
Your constrains must be activated :
switchTheme.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
I have UIButton, which title is dynamic changes. Button size should changes with title size and will be equal title size.
How to do this programmatically in Swift?
To have your button use its intrinsic content size and automatically resize based upon its text, use Auto Layout to position the button. Only set constraints to position the button and iOS will use the size of the text to determine the width and height of the button.
For example:
let button = UIButton()
// tell it to NOT use the frame
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Hello", for: .normal)
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
This also works if you create the button in the Storyboard. Again, only give constraints to place the button and it will resize to accommodate the text.
Follow below steps(its not a proper solution but you can solve your problem by doing like this )
Create a UILabel (because UILabel adjust its height and width depends on the text)
UIlabel number of line to 1
Create a UIButton over UILabel
Set button title to ""
Set button's constraint : Align button's top and leading to UILabel and equals width and height
Hope this will works for you :)
You can get UIButton's width and Height dynamically with its title.
With the help of, NSString's Size property we can achieve this.
let buttonNAme = [" hi ", "welcome", "Login", "Forgot Password ??", "New to here. Sign up??"]
var yPos = CGFloat()
override func viewWillAppear(_ animated: Bool) {
yPos = 40
for i in 0..<buttonNAme.count
{
self.view.addSubview(addingCustomButton(buttonTitle: buttonNAme[i], buttonFontSize: 15, buttonCount: i))
}
}
func addingCustomButton(buttonTitle : String, buttonFontSize: CGFloat, buttonCount : Int) -> UIButton
{
let ownButton = UIButton()
ownButton.setTitle(buttonTitle, for: UIControlState.normal)
ownButton.titleLabel?.font = UIFont.systemFont(ofSize: buttonFontSize)
let buttonTitleSize = (buttonTitle as NSString).size(attributes: [NSFontAttributeName : UIFont.boldSystemFont(ofSize: buttonFontSize + 1)])
ownButton.frame.size.height = buttonTitleSize.height * 2
ownButton.frame.size.width = buttonTitleSize.width
ownButton.frame.origin.x = 30
yPos = yPos + (ownButton.frame.size.height) + 10
ownButton.frame.origin.y = yPos
ownButton.tintColor = UIColor.white
ownButton.backgroundColor = .brown
ownButton.tag = buttonCount
ownButton.setTitleColor(UIColor.darkGray, for: UIControlState.highlighted)
ownButton.addTarget(self, action: #selector(ownButtonAction), for: UIControlEvents.touchUpInside)
return ownButton
}
func ownButtonAction(sender: UIButton)
{
print("\n\n Title \(sender.titleLabel?.text) TagNum \(sender.tag)")
}
Output
Just Constraint it's origin, and the size will fit it's button title