I noticed certain button sizes look great on one the iPhone 5 simulator but do not look as good on the iPhone 6 simulator and this is because the heights or constraints that I place on the UIButtons end up leaving a lot of blank space down the bottom of my App Screen.
I would like to have a button that is 40% of the screen size regardless of what device I am simulating on.
Any ideas on how to make the size of the button stay 40% of the screen size regardless of the device?
Ctrl drag from button to superview and select Equal Widths
Open Size Inspector edit Equal Widths constraint and set multiplier to 0.4.
And you will see something like this:
Add missing constraints and update frames.
You can't set a constraint to be 40% of the screen height. However, if the button always has a 20px leading and trailing to the superview you could use that width and set an aspect ratio height.
Another way could be to use UIScreen.mainScreen().bounds.size.height * 0.4 for your button height.
A third way is to use the button's superview to set the height. Assuming that your button is a direct child of a UIViewController's view: view.bounds.size.height * 0.4.
There's probably a bunch of other ways to do this as well but none of them will involve setting the height to an actual percentage as far as I'm aware.
This function gives you the bounds of the screen
var bounds = UIScreen.mainScreen().bounds
And then you can set the button width multipliying 0.4 x bounds.size.width
Regards
Swift 4.2
In code it's really easy:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height * 0.4).isActive = true
}
or use this instead of current heightAnchor:
button.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.4).isActive = true
hope this help :)
Related
I programmatically create buttons using this code
let button: NSButton = NSButton()
button.action = #selector(ViewController.eject)
button.bezelStyle = .texturedSquare
buttons.append(button)
stackView.addView(buttons[i], in: .leading)
The problem is that those buttons appear like that
I would like to set the (x , y) position and the size of those buttons. For example, I would like to set their width to fill the whole space.
I tried multiple solutions:
button.frame = CGRect(x: 0.0,y: 0.0,width: 100.0,height: 100.0)
and play with the arguments, but still their size don't change.
I then tried to add them as SubViews to stackView, but obviously they overlap with each other.
Any suggestion?
To fix those buttons you first have to modify the StackView element in the storyboard.
Go to the menu of the storyboard and modify it like that
With the "fill" distribution you let the buttons to fill the entire width of the stackview.
With "spacing" you define how much space there has to be between each button.
Then you go there
To give some space to the buttons just play with the Edge Insets values.
Then set the horizontal Hugging Propriety to 250.
The last thing you have to do is to add this code in your ViewController file.
button.setContentHuggingPriority(NSLayoutConstraint.Priority.init(1), for: .horizontal)
button.translatesAutoresizingMaskIntoConstraints = false
In this way you'll the the horizontal Hugging Propriety of the button to 1.
Here's the buttons I created
I have the following subview that I would like to centered in horizontally on the screen and set the vertical distance manually.
let customImageView = AnimationView(name: "image")
customImageView.frame = CGRect(x: -140, y: 40, width: 700, height: 700)
customImageView.contentMode = .scaleAspectFill
self.view.addSubview(customImageView)
Is it possible to ensure that the vertical distance set will account if the screen size of the phone is smaller.
This sounds like a job for Auto Layout (https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/index.html), which is Apple's constraint-based system made specifically to dynamically size views in relation to other views and screen sizes. In your case, you could set a greater than/less than to constraint to the top of the view and your subview, so that it will be at least a certain amount of spacing with the option to grow if the screen is larger/smaller.
If you would like to still do frame based layouts (e.g. creating a CGRect/frame), you'll have to do the math yourself based on the view.frame.size.height value. Be aware that a view controller's view frame might not be set before viewDidLayoutSubviews is called.
I would like to create a vertical stackview with 3 elements in it.
I want a bit more space only between the 2nd and the last element. So I thought about adding to the last element :
mylastelement.layoutMargins = UIEdgeInsets(top:30, left:0,bottom:0, right:0)
But the layoutmargins are not applied in my stackview. Is there any easy way to achieve that (Id like to avoid to modify the last element inner height).
EDIT : I just tried to increase 2nd element height (+50) within its frame by doing :
my2ndElementLabel.sizeToFit()
my2ndElementLabel.frame = CGRect(x:my2ndElementLabel.frame.origin.x,y:lmy2ndElementLabel.frame.origin.y,
width:my2ndElementLabel.frame.width, height:my2ndElementLabel.frame.height + 50)
but it has no effect.
EDIT2 : I tried to add a random view to my UIStackView, but the the view is just ignored ! May have missed something in understanding how UIKit work ?... :
let v = UIView(frame:CGRect(x:0,y:0,width:100,height:400))
v.backgroundColor = .red
myStackView.addArrangedSubview(v)
//...
Here is an extension I made that helps to achieve fast such margins :
extension UIStackView {
func addArrangedSubview(_ v:UIView, withMargin m:UIEdgeInsets )
{
let containerForMargin = UIView()
containerForMargin.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: containerForMargin.topAnchor, constant:m.top ),
v.bottomAnchor.constraint(equalTo: containerForMargin.bottomAnchor, constant: m.bottom ),
v.leftAnchor.constraint(equalTo: containerForMargin.leftAnchor, constant: m.left),
v.rightAnchor.constraint(equalTo: containerForMargin.rightAnchor, constant: m.right)
])
addArrangedSubview(containerForMargin)
}
}
What you could do is set a custom spacing between the second and third element.
myStackView.setCustomSpacing(30.0, after: my2ndElementLabel)
In the same general vein, you can constrain the top (or bottom) anchor of your view relative to the corresponding edge of any view in which it's embedded. What's ugly being somewhat a matter of taste, I find autolayout constraints easy to use and easy to reason about.
A simple example from Mac OS rather than iOS:
let button = ControlFactory.labeledButton("Filter")
addSubview(button)
button.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20).isActive = true
button.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
This particular code lives in the view initializer, and positions a button in the middle of a view, 20 points up from the bottom.
I found myself : It looks like UIStackView doesn't work at all with old sizing system (with .frame). It seems you have to constraint height and width, and StackView will constraint left/top/right/bottom position for you when you add the arrangedSubview.
My second view was a label : I wanted a margin of 40, under the text. So i first computed the label height into its .frame property, and constraint the height at frame.height + 40(= my margin)
labelDesc.sizeToFit()
labelDesc.heightAnchor.constraint(equalToConstant:40).isActive = true
I find my own solution utterly ugly though. I'm sure UIKit provide a better way to achieve such a simple goal, without having to make these kind of DIY solutions. So please if you're used to work with UIKit, tell me if there is any better solution.
Consider adding a "margin" by inserting a correctly-sized UIView within the Stack View as needed.
If you need a 40px margin between 2 specific elements... add a UIView with a height constraint of 40px. Assign a clearColor background to make it invisible.
You can add IBOutlets to this view and hide it as you would any other item in the Stack View.
To start with, here's a mockup of the layout I'm trying to accomplish in Swift.
And here's what I have so far,
So here's the problem. Notice that in the second image the green UIView overflows exceeding the TableViewCell height.
In my Main TableViewController class I've defined each cell to be 120 pixels in height, and the green UIView 10 pixels short of the cell height with the 10 pixels on top as a separator between subsequent cells.
Cell height definition:
var itemHeight = [CGFloat](count: 2, repeatedValue: 120.0)
UIView constraints:
foregroundView.topAnchor.constraintEqualToAnchor(foregroundView.superview?.topAnchor, constant: 10).active = true
foregroundView.leftAnchor.constraintEqualToAnchor(foregroundView.superview?.leftAnchor, constant: 20).active = true
foregroundView.widthAnchor.constraintEqualToAnchor(foregroundView.superview?.widthAnchor, constant: -40).active = true
foregroundView.heightAnchor.constraintEqualToConstant(110).active = true
Any ideas on what might be causing the UIView overflow?
Thanks in advance!
Please let me know if you'd like to see the code in context :)
Remove two lines of code from your project:
foregroundView.layoutIfNeeded()
And
containerView.layoutIfNeeded()
They are forcing an untimely laying out of content that is causing your problem.
I've been using auto layout so far, so I'm not sure how this works.
I have a simple button I want to be close to my down right corner.
How can its position programmatically in swift?
Here you go:
let button = UIButton()
button.setTitle("Button", forState: .Normal)
button.backgroundColor = UIColor.blackColor()
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor, constant: -20).active = true
button.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: -20).active = true
Setting the translatesAutoresizingMaskIntoConstraints to false is necessary. By default, the property is set to true for any view you programmatically create.
In this case you need only 2 constraints (bottomAnchor and trailingAnchor). Since the button has the intrinsicContentSize (the natural size based on the title and stuff), you don't have to add constraints for the height/width.
Also you might want to use the layoutMarginsGuide to pin the button to the edges of the superview. This way you don't need to specify the constants:
superView.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
let margins = superView.layoutMarginsGuide
button.bottomAnchor.constraintEqualToAnchor(margins.bottomAnchor).active = true
button.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true
Note that if the view is a view controller’s root view, then the system sets and manages the margins. The top and bottom margins are set to 0 points. The side margins vary depending on the current size class, but can be either 16 or 20 points. You cannot change these margins.
You can use autolayout programmatically.
SnapKit is a nice swift library to make the autolayour code more easy to use and readable.
Otherwise you can always use it the normal way. These are two nice tutorials on it:
1
2