I am attempting to get a UIView courseView to autolayout. I would like to have the proportions of the UIView remain and fill up until the outermost edges are 15 point from the edge of the superview.
For some reason courseView fills the entire superview (minus the 15 points) and does not resize to fit. So some of it does not show and is cut off.
self.view.addSubview(courseView!)
let aspectConstraint = NSLayoutConstraint(item: courseView,
attribute: .Height,
relatedBy: .Equal,
toItem: courseView,
attribute: .Width,
multiplier: courseView.frame.size.height / courseView.frame.size.width,
constant: 0.0)
aspectConstraint.active = true
let topConstraint = courseView.topAnchor.constraintGreaterThanOrEqualToAnchor(topLayoutGuide.bottomAnchor, constant: 15)
topConstraint.active = true
let leadingConstraint = courseView.leadingAnchor.constraintLessThanOrEqualToAnchor(view.leadingAnchor, constant: 15)
leadingConstraint.active = true
let trailingConstraint = courseView.trailingAnchor.constraintGreaterThanOrEqualToAnchor(view.trailingAnchor, constant: -15)
trailingConstraint.active = true
let bottomConstraint = courseView.bottomAnchor.constraintLessThanOrEqualToAnchor(view.bottomAnchor, constant: -15)
bottomConstraint.active = true
Any ideas? Thanks!
Just disable translatesAutoresizingMaskIntoConstraints before adding the constraints and it should work just fine.
courseView.translatesAutoresizingMaskIntoConstraints = false;
And by the way, you do not need the aspectConstraint because that will most probably break the constratints (it did for me, when I tried).
Related
I am trying to set a horizontal center constraint of a view with multiplier programmatically. But what I get is always a constraint with 1.0 as the multiplier. This is what I did:
private func createHalfCenteredView() {
let newView = UIView(frame: .zero)
newView.backgroundColor = .systemTeal
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let top = newView.topAnchor.constraint(equalTo: view.topAnchor)
let bottom = newView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
let width = newView.widthAnchor.constraint(equalToConstant: 100)
let center = newView.centerXAnchor.constraint(equalToSystemSpacingAfter: view.centerXAnchor,
multiplier: 0.5)
NSLayoutConstraint.activate([top, bottom, width, center])
newView.setNeedsUpdateConstraints()
view.setNeedsLayout()
view.layoutIfNeeded()
}
I tried using lessThanOrEqualToSystemSpacingAfter instead of equalToSystemSpacingAfter but it is still the same. The multiplier is always 1.0 or exactly in the middle.
Can anybody help me with this? Thanks.
You can't use multipliers using helpers functions, try this way
let center = NSLayoutConstraint(item: newView, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 0.5, constant: 0)
Refer to answer
I'm having trouble with the positioning of my button. I am trying to position my button on the bottom right of my screen. I am new with auto layouts. The button appears currently on the top left of the screen.
Here is my code:
add markers
add the map view
add the button
heres where I add markers:
func secondfunction() {
for x in names{
let url1 = URL(string: url: ", url1)
let data1 = try? Data(contentsOf: url1!) //make sure your image in this url does exist
//self.imagesOne = UIImage(data: data1!)
self.images.append(UIImage(data: data1!)!)
}
self.loadFunction()
}
heres where I load map and add button:
func loadFunction()
{
mapView = MGLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.centerCoordinate = CLLocationCoordinate2D(latitude:xCoord, longitude: yCoord)
mapView.zoomLevel = 15
mapView.delegate = self
view.addSubview(mapView)
var pointAnnotations = [MGLPointAnnotation]()
for coordinate in locationsList {
let location: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
let point = MGLPointAnnotation()
point.title = "Tap here"
point.coordinate = location
pointAnnotations.append(point)
}
self.tabBarController?.tabBar.isHidden = false
mapView.addAnnotations(pointAnnotations)
button.setBackgroundImage(UIImage(named:"compass.png"), for: .normal)
button.addTarget(self, action: #selector(btnPressed), for: UIControl.Event.touchUpInside)
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
let widthContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 40)
let heightContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 40)
let xContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.bottomMargin, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.bottomMargin, multiplier: 1, constant: -80)
let yContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1, constant: -80)
NSLayoutConstraint.activate([heightContraints,widthContraints,xContraints,yContraints])
}
Give your button constraint to bottom
like this .. this is working tested code
override func viewDidLoad() {
super.viewDidLoad()
button.backgroundColor = .red
self.view.addSubview(button)
}
override func viewDidLayoutSubviews() {
button.translatesAutoresizingMaskIntoConstraints = false
let widthContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 40)
let heightContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 40)
let xContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.bottomMargin, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.bottomMargin, multiplier: 1, constant: -20)
let yContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.trailing, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.trailing, multiplier: 1, constant: -20)
NSLayoutConstraint.activate([heightContraints,widthContraints,xContraints,yContraints])
}
if you want to position it right give it constraint from right margin
change constant according to your design preference
Remove this top constraint
let xContraints = NSLayoutConstraint(item: button, attribute: NSLayoutConstraint.Attribute.topMargin, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.topMargin, multiplier: 1, constant: 20)
and add a bottom constraint
NSLayoutConstraint.activate([
button.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
button.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20),
button.widthAnchor.constraint(equalToConstant: 40),
button.heightAnchor.constraint(equalToConstant: 40)
])
It looks like you are doing constraints in code. If I may, could I offer a more recent alternative? Use anchors, which are much more easier and is part of any subclass of UIView.
There are several anchors for a view - top, bottom, left or leading, right or trailing, center x and Y, and height/width are the most used.
Now, for any view, you need to do two things:
Position it. In your case you only need to position your button in the (a) lower or bottom (b) right or trailing.
If it doesn't have an intrinsic size (search for a better definition than I can give) give your view a hight and width.
So in your case, lets say you wish to position a UIButton that is offset 10 points away from the bottom right of the screen. (Keep in mind that Apple has introduced "safe area insets" but that's a subject for another question. Again, search for it and you'll find lots of examples.)
button.translatesAutoresizingMaskIntoConstraints = false
Always remember to do this!
Now let's give your button a size:
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
Finally, position it:
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
That's it! The code is less verbose and thus easier to read.
Beyond the basics (and safe areas), you can also programmatically do two more things:
Change the constants (and for some views, the multiplier) as long as you name a constraint.
Selectively activate/deactivate an array of constraints, again, as long as you set them up in an array.
I find using anchors much easier, and have a different layout based on portrait or landscape by using arrays.
In my swift app I have a view with a UILabel and a UIButton. In the storyboard it looks like this:
I know I can group those two elements and then put constraints on that group, but that will only work when the UILabel has constant width.
I want to display this group like this:
| label X |
or - when the label is longer, like this:
| longerlabel X |
How should I apply constraints to get that effect?
At first I considered UILayoutGuides, but it's much easier than that, as long as you are willing to code a few things. Use UILayoutAnchors, centerX, and a multiplier:
myLabel.centerXAnchor.constraint(equalTo: myView.centerXAnchor, multiplier: 0.33)
myButton.centerXAnchor.constraint(equalTo: myView.centerXAnchor, multiplier: 0.667)
Of course, you need to layout more than this (vertical position in particular) and yes, you can use UILayoutGuides for equal spacing. But as long as you are using auto layout and understand how to set up IBOutlets for the things you need to code for that way, this will work.
BTW, you can be exact, as in reference the superview in code to center it perfectly.)
you should use a regular UIView as a container. you could set your views up in code like this:
// configure the content
let labelText = "Label"
let buttonTitle = "X"
// setup the views
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = labelText
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle(buttonTitle, for: .normal)
button.setContentCompressionResistancePriority(label.contentCompressionResistancePriority(for: .horizontal) + 1, for: .horizontal)
let container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
container.layer.borderColor = UIColor.lightGray.cgColor
container.layer.borderWidth = 1
// add the views
container.addSubview(label)
container.addSubview(button)
view.addSubview(container)
// create the container constraints
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "|[lbl]-[btn]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: ["lbl": label, "btn": button]))
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|[btn]|", options: [], metrics: nil, views: ["btn": button]))
// center the container
NSLayoutConstraint(item: container, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: container, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
// make sure the container does not extend the view's width
NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: view, attribute: .leading, multiplier: 1, constant: 20).isActive = true
feel free to ask if anything is unclear! this is the result btw:
In viewDidLoad, I instantiate a UIView and add a gradient layer to that UIView.
This works well on iPhone, but on iPad, UIView does not stretch out to fill the entire screen. Please note the following screenshot for iPad sim.
iPad simulator screenshot
I have attempted adding constraints both programmatically and by using XCode.
It seems that once I add the CAGradientLayer, the UIView does not conform to the constraints that I set, either programmatically or using XCode tools.
Sample Code:
gradientLayer = CAGradientLayer()
gradientLayer.frame = self.view.bounds
gradientLayer.locations = [0.5, 1.0]
gradient_View.backgroundColor = UIColor.blueColor()
let color1 = self.opus_Page_Background_Color.CGColor as CGColorRef
let color4 = self.opus_Page_Tertiary_Color.CGColor as CGColorRef
gradientLayer.colors = [color1, color4]
self.gradient_View.frame = self.view.bounds
self.gradient_View.layer.insertSublayer(gradientLayer, atIndex: 0)
gradient_View.translatesAutoresizingMaskIntoConstraints = false
let leadingConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
leadingConstraint.active = true
view.addConstraint(leadingConstraint)
let trailingConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
trailingConstraint.active = true
view.addConstraint(trailingConstraint)
let widthConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
view.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: gradient_View, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 100)
view.addConstraint(heightConstraint)
NSLayoutConstraint.activateConstraints([leadingConstraint, trailingConstraint, widthConstraint, heightConstraint])
Try changing the last two constraints. Instead of matching width and height, pin it to Top & Bottom. The problem probably is that in viewDidLoad, your view has width = X, height = Y, but that's not the final layout.
If that does not work, try adding it in viewDidLayoutSubviews.
Hope it helps!
You have added leading and trailing constraint which means you have got the width of the view. So there is no need to add width constraint at all. Remove width from storyboard or programmatically which ever you are using.
Also i could see a missing top constrai nt. Resolve that also
I have a parent, called infoView. It has two children: subLabel and tinyImageView. I'd like both of these children to be size 30.0, with subLabel first, followed by 10 pixel padding, followed by the tinyImageView.
For some reason, my tinyImageView is not respecting ANY of the constraints I put below. Even the height/width is not respected.
let boxSize = infoView.frame.size.height //30
let subLabel = UILabel()
subLabel.frame = CGRectMake(0, 0, boxSize, boxSize)
subLabel.layer.cornerRadius = 5.0
subLabel.clipsToBounds = true
subLabel.backgroundColor = logoColor(1)
subLabel.text = String(post.subscribers)
subLabel.font = UIFont(name: "Lato-Bold", size: 13.0)
subLabel.textColor = UIColor.whiteColor()
subLabel.textAlignment = .Center
infoView.addSubview(subLabel)
//Image
let tinyImageView = UIImageView(image:UIImage(named: "MO.jpg"))
tinyImageView.layer.cornerRadius = 2.5
tinyImageView.clipsToBounds = true
infoView.addSubview(tinyImageView)
let widthConstraint = NSLayoutConstraint(item: tinyImageView, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: boxSize)
tinyImageView.addConstraint(widthConstraint)
let heightConstraint = NSLayoutConstraint(item: tinyImageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: boxSize)
tinyImageView.addConstraint(heightConstraint)
print("------------")
let horizontalConstraint = NSLayoutConstraint(item: subLabel, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: tinyImageView, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 10)
infoView.addConstraint(horizontalConstraint)
let verticalConstraint = NSLayoutConstraint(item: subLabel, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: tinyImageView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
verticalConstraint.active = true
The image turns out way too big, and the horizontal/vertical constraints don't work at all. Currently, the image overlaps subLabel, as if it was just added without any constraints.
As #Paulw11 stated in the comments, you need to set translatesAutoResizingMaskIntoConstraints to false for programmatically created views. Also, your constraints will resize the frame for your UIImageView, but they won't scale the image. For that, you need to set the contentMode property:
tinyImageView.translatesAutoResizingMaskIntoConstraints = false
tinyImageView.contentMode = .ScaleToFill
Values you probably want to consider for contentMode are .ScaleToFill (scale to fit area distorting aspect ratio if necessary), .ScaleAspectFit (maintain aspect ratio and leave part of the view as transparent if necessary) and .ScaleAspectFill (maintain aspect ratio and clip part of image if necessary).
From your code, I just saw one activation for constraint verticalConstraint.
active all the constraints you add.
if your constraints got conflicted,
Set translatesAutoResizingMaskIntoConstraints to false(usually if you set constraints programmatically, you set it to false)