I have the following code to simply center a red square using AutoLayout constraints programmatically in my ViewController's view:
class ViewController: UIViewController {
let square: UIView
required init?(coder aDecoder: NSCoder) {
let squareFrame = CGRectMake(0.0, 0.0, 500.0, 500.0)
self.square = UIView(frame: squareFrame)
super.init(coder: aDecoder)
}
override func viewDidLoad() {
self.view.addSubview(self.square)
self.square.backgroundColor = UIColor.redColor()
self.view.backgroundColor = UIColor.blueColor()
print(self.square)
setupConstraints()
print(self.square)
}
func setupConstraints() {
self.square.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal,
toItem: self.square, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant:0).active = true
NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal,
toItem: self.square, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant:0).active = true
}
}
The resulting screen however only shows the blue background, no sign of the red square... Even when using the view debugging feature in Xcode it can't be seen.
If I comment out setupConstraints(), it works as "expected" with the original frame that I gave the square during initialisation.
By the way, both print statements have the exact same output:
<UIView: 0x7ff1c8d3f3e0; frame = (0 0; 500 500); layer = <CALayer: 0x7ff1c8d04c00>>
How can this be when the square is nowhere to be seen?
Update:
The issue remains when I am adding width and height constraints as suggested by #HaydenHolligan in setupConstraints():
func setupConstraints() {
self.square.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal,
toItem: self.square, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant:0).active = true
NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal,
toItem: self.square, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant:0).active = true
// the following lines have no effect with respect to the issue mentioned aboove
let sizeFormat = "[square(100#100)]"
let size = NSLayoutConstraint.constraintsWithVisualFormat(sizeFormat, options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: ["square": self.square])
self.view.addConstraints(size)
}
Try to change your setupConstraints func to this :
func setupConstraints() {
self.square.translatesAutoresizingMaskIntoConstraints = false
let centerX = NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal,toItem: self.square, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant:0)
let centerY = NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal,toItem: self.square, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant:0)
let squareWidth = NSLayoutConstraint(item: self.square, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant:500)
let squareHeight = NSLayoutConstraint(item: self.square, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant:500)
self.view.addConstraints([centerX , centerY ,squareWidth , squareHeight])
}
Related
I am unable to understand where to add subview so that the button can lie in middle of the existing views.
This is work for me. (swift 4.0)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let yourView = self.view
addButtonOnCentral(yourView!)
}
func addButtonOnCentral(_ view:UIView) {
let btn = UIButton.init()
btn.backgroundColor = UIColor.yellow
btn.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(btn)
let widthConstraint = NSLayoutConstraint(item: btn, attribute: .width, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 20)
let heightConstraint = NSLayoutConstraint(item: btn, attribute: .height, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 30)
let xConstraint = NSLayoutConstraint(item: btn, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0)
let yConstraint = NSLayoutConstraint(item: btn, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
NSLayoutConstraint.activate([widthConstraint, heightConstraint, xConstraint, yConstraint])
}
func setupView()
{
self.blueView = UIView()
self.blueView?.backgroundColor = UIColor.blueColor()
self.blueView?.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.blueView!)
let blueViewCenterXConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 0.6, constant: 0)
let blueViewCenterYConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0)
let blueViewWidthConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 0.6, constant: 150)
let blueViewHeightConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 150)
self.addConstraints([blueViewCenterXConstraint,blueViewCenterYConstraint,blueViewWidthConstraint,blueViewHeightConstraint])
self.redView = UIView()
self.redView?.backgroundColor = UIColor.redColor()
self.redView?.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(self.redView!)
let redViewCenterXConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.TrailingMargin , multiplier: 0.5, constant: 0)
let redViewCenterYConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Top, multiplier: 0.5, constant: 0)
let redViewWidthConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Width, multiplier: 0.5, constant: 150)
let redViewHeightConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Height, multiplier: 0.5, constant: 150)
self.addConstraints([redViewCenterXConstraint,redViewCenterYConstraint,redViewWidthConstraint,redViewHeightConstraint])
}
I have applied above code for Creating and Setting Auto Layout Constraint for UITextView for Universal App. I want to Put these both UITextView beside Each other for Every Device with the space of 10 between them horizontally and centered in Vertically. Will Anybody please be Grateful to fix mentioned issue, which would be helpful a lot to me.
I've taken the liberty of looking at you previous question and furthermore I've used NSLayoutAnchors (described here) because I think they are easier to read.
Based on the above, this UIView subclass:
import UIKit
class UIViewUsingTextField: UIView {
let width: CGFloat = 150.0
var blueview = UIView()
var redview = UIView()
init() {
super.init(frame: CGRect.zero)
self.backgroundColor = UIColor.whiteColor()
self.setupView()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
setupView()
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
func setupView() {
//add blue view
blueview.backgroundColor = UIColor.blueColor()
blueview.translatesAutoresizingMaskIntoConstraints = false
addSubview(blueview)
//center the blue view and move it 1/2 view width to the left
blueview.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor, constant: -(width/2)).active = true
blueview.centerYAnchor.constraintEqualToAnchor(self.centerYAnchor).active = true
blueview.widthAnchor.constraintEqualToConstant(width).active = true
blueview.heightAnchor.constraintEqualToConstant(width).active = true
//add red view
redview.backgroundColor = UIColor.redColor()
redview.translatesAutoresizingMaskIntoConstraints = false
addSubview(redview)
//place red view 10 px to the right of blue view
redview.leadingAnchor.constraintEqualToAnchor(blueview.trailingAnchor, constant: 10.0).active = true
redview.centerYAnchor.constraintEqualToAnchor(blueview.centerYAnchor).active = true
redview.widthAnchor.constraintEqualToAnchor(blueview.widthAnchor).active = true
redview.heightAnchor.constraintEqualToAnchor(blueview.heightAnchor).active = true
}
}
Gives me this layout
Hope this resembles what you were after.
I have included a solution using your code, but would also like to show you how it is done using constraintWithVisualFormat, because I think it reads easier.
Solution for you code:
Note that the width are set to a fixed value here. Not sure if this is really what you want.
func setupView()
{
self.blueView = UIView()
self.blueView?.backgroundColor = UIColor.blueColor()
self.blueView?.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.blueView!)
let blueViewCenterXConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: -5)
let blueViewCenterYConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1.0, constant: 0)
let blueViewWidthConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: 150)
let blueViewHeightConstraint = NSLayoutConstraint(item: self.blueView!, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 150)
self.view.addConstraints([blueViewCenterXConstraint,blueViewCenterYConstraint,blueViewWidthConstraint,blueViewHeightConstraint])
self.redView = UIView()
self.redView?.backgroundColor = UIColor.redColor()
self.redView?.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.redView!)
let redViewCenterXConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Trailing , multiplier: 1, constant: 10)
let redViewCenterYConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
let redViewWidthConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 1)
let redViewHeightConstraint = NSLayoutConstraint(item: self.redView!, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: blueView, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 1)
self.view.addConstraints([redViewCenterXConstraint,redViewCenterYConstraint,redViewWidthConstraint,redViewHeightConstraint])
}
Solution using visual constraints
let views = ["redView": redView!, "blueView": blueView!]
//setup the horizontal constraints to have 15 leading and trailing constraints, equal widths for blueView and redView and 10 spacing in between.
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-15-[blueView(==redView)]-10-[redView]-15-|", options: .AlignAllCenterY, metrics: nil, views: views))
//set the height of the two views
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[blueView(150)]", options: .AlignAllCenterX, metrics: nil, views: views))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[redView(150)]", options: .AlignAllCenterX, metrics: nil, views: views))
//align the two views relative to the centre of the superview.
blueView!.centerYAnchor.constraintEqualToAnchor(view!.centerYAnchor, constant: 0).active = true
redView!.centerYAnchor.constraintEqualToAnchor(blueView!.centerYAnchor).active = true
Replace self.view with self for your example
I added the following code to center a programmatically added view:
let horizontalConstraint = NSLayoutConstraint(item: newView!, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
view.addConstraint(horizontalConstraint)
It doesn't work. The view is not centered. It is on the left still.
EDIT:
override func viewDidLoad() {
super.viewDidLoad()
newView = LineChartView(frame: CGRectMake(0, 0, 50, 50))
newView?.delegate = self
newView?.drawBordersEnabled = true
newView?.noDataText = "No Data"
newView?.noDataTextDescription = "No Data"
newView?.borderColor = UIColor.blackColor()
self.view.addSubview(newView!)
You need to pick a side my friend, If you are using auto layout, don't initialise your objects with a frame. Try something like this...
var newView:LineChartView!
override func viewDidLoad() {
super.viewDidLoad()
newView = LineChartView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.delegate = self
newView.drawBordersEnabled = true
newView.noDataText = "No Data"
newView.noDataTextDescription = "No Data"
newView.borderColor = UIColor.blackColor()
self.view.addSubview(newView)
let width = NSLayoutConstraint(item: newView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 50)
let height = NSLayoutConstraint(item: newView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 50)
newView.addConstraint(width)
newView.addConstraint(height)
let x = NSLayoutConstraint(item: newView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0)
let y = NSLayoutConstraint(item: newView, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1, constant: 0)
self.view.addConstraint(x)
self.view.addConstraint(y)
}
If your LineChartView object is a subclass of UIView then this should work, and you should have a 50x50 object in the middle of your superview.
If you are going to be doing constraints like this in code you should consider using Apples Visual Formatting Language.
In my project I defined a xib and a ViewController that will contain the xib.
class FeedDetailViewController: UIViewController, FeedDetailDelegate {
var feedDetail: FeedDetail!
override func viewDidLoad() {
super.viewDidLoad()
feedDetail = (NSBundle.mainBundle().loadNibNamed("FeedDetail", owner: self, options: nil)[0] as? UIView)! as! FeedDetail
feedDetail.delegate = self;
self.view.addSubview(feedDetail)
self.adjustContraints()
}
func adjustContraints() {
feedDetail.sizeThatFits(self.view.frame.size)
var constraint = NSLayoutConstraint(item: feedDetail,
attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: feedDetail,
attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: feedDetail,
attribute: NSLayoutAttribute.Trailing, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 0)
view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: feedDetail,
attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: 0)
view.addConstraint(constraint)
self.view.layoutIfNeeded()
}
What I want is simply the xib at "fullscreen", but xib borders are not "sticked" to the main view of the controller.
Border legend:
red: self.view
yellow: feedDetail
others: element inside feedDetail (test only)
Try to call this method before adding constraints.
feedDetail.setTranslatesAutoresizingMaskIntoConstraints(false)
I've got a situation where I would like a view to be centered in its superview, remain square, but fill as much height as possible without going off the edge, i.e., it should look at the available vertical and horizontal space, choosing the smallest between the 2.
There are 2 other views, one below and one above, that will both be either a button or label. The bottom/top of these views should be attached to the top/bottom of the central view. I can get this to work, to an extent, but I'll explain my issue below, and what I've got so far:
Top label has:
.Top >= TopLayoutGuide.Bottom
.Top = TopLayoutGuide.Bottom (priority 250)
.Right = CentralView.Right
Central view has:
Center X and Y = Superview Center X and Y
.Height <= Superview.Width * 0.9
.Width = self.Height
.Top = TopLabel.Bottom
Bottom button has:
.Right = CentralView.Right
.Top = CentralView.Bottom
.Bottom <= (BottomLayoutGuide.Top - 16)
Running this seems fine, and produces the desired results:
However, if I make the view an instance of my custom class and add a UIButton subview, it all goes wrong. In this class I perform:
self.topLeftButton = CustomButtonClass()
self.topLeftButton.setTranslatesAutoresizingMaskIntoConstraints(false)
self.addSubview(self.topLeftButton)
self.addConstraints([
NSLayoutConstraint(item: self.topLeftButton, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.topLeftButton, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: self.topLeftButton, attribute: .Height, relatedBy: .Equal, toItem: self, attribute: .Height, multiplier: 0.5, constant: 0),
NSLayoutConstraint(item: self.topLeftButton, attribute: .Width, relatedBy: .Equal, toItem: self.topLeftButton, attribute: .Width, multiplier: 1, constant: 0)
])
Using this code the view collapses down to the following:
I can't figure out why this is. I've made a few small tweaks here and there, but not managed to get it to work as desired. If I add the same button in IB the view wants to collapse again, and it's as if the button will not grow in height.
In real life I wouldn't subclass UIButton, but have done in my answer, as that is what the question indicated. UIButton works best through composition. So maybe better to create a UIButton, then modify its properties.
class FooViewController: UIViewController {
override func viewDidLoad() {
var view = CustomView()
view.backgroundColor = UIColor.darkGrayColor()
var label = UILabel()
label.text = "Label"
var button = UIButton.buttonWithType(.System) as UIButton
button.setTitle("Button", forState: .Normal)
view.setTranslatesAutoresizingMaskIntoConstraints(false)
label.setTranslatesAutoresizingMaskIntoConstraints(false)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(view)
self.view.addSubview(label)
self.view.addSubview(button)
// The width should be as big as possible...
var maxWidthConstraint = NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: view.superview, attribute: .Width, multiplier: 1, constant: 0);
// ... but not at the expense of other constraints
maxWidthConstraint.priority = 1
self.view.addConstraints([
// Max width, if possible
maxWidthConstraint,
// Width and height can't be bigger than the container
NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .LessThanOrEqual, toItem: view.superview, attribute: .Width, multiplier: 1, constant: 0),
NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .LessThanOrEqual, toItem: view.superview, attribute: .Height, multiplier: 1, constant: 0),
// Width and height are equal
NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1, constant: 0),
// View is centered
NSLayoutConstraint(item: view, attribute: .CenterX, relatedBy: .Equal, toItem: view.superview, attribute: .CenterX, multiplier: 1, constant: 0),
NSLayoutConstraint(item: view, attribute: .CenterY, relatedBy: .Equal, toItem: view.superview, attribute: .CenterY, multiplier: 1, constant: 0),
])
// Label above view
self.view.addConstraints([
NSLayoutConstraint(item: label, attribute: .Top, relatedBy: .GreaterThanOrEqual, toItem: label.superview, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: label, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: label, attribute: .Right, relatedBy: .LessThanOrEqual, toItem: view, attribute: .Right, multiplier: 1, constant: 0),
])
// Button below view
self.view.addConstraints([
NSLayoutConstraint(item: button, attribute: .Bottom, relatedBy: .LessThanOrEqual, toItem: button.superview, attribute: .Bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: button, attribute: .Right, relatedBy: .LessThanOrEqual, toItem: view, attribute: .Right, multiplier: 1, constant: 0),
])
}
}
class CustomView: UIView {
required init(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override init() {
super.init()
var button = CustomButton()
button.setTitle("Custom Button", forState: UIControlState.Normal)
button.setTranslatesAutoresizingMaskIntoConstraints(false)
self.addSubview(button)
// Custom button in the top left
self.addConstraints([
NSLayoutConstraint(item: button, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: button, attribute: .Left, relatedBy: .Equal, toItem: self, attribute: .Left, multiplier: 1, constant: 0),
])
}
}
class CustomButton: UIButton {
required init(coder: NSCoder) {
super.init(coder: coder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override init() {
super.init()
self.backgroundColor = UIColor.greenColor()
}
}