Programmatically adding constraints in Swift does not work - ios

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.

Related

How to add UIButton in between two views without using storyboard?

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])
}

Dynamically positioning UILabel to bottom of content view with Swift?

I was trying to position my dynamically created UILabels to my contentView's bottom. I'm using HTML Parser called Fuzi to catch HTML tags and creating UILabels based on them;
func stringFromHTML( _ string: String?)
{
do{
let doc = try HTMLDocument(string: string!, encoding: String.Encoding.utf8)
if let root = doc.body {
for element in root.children {
if element.tag == "h2" {
// Create new label
let label = UILabel()
label.text = element.stringValue
label.numberOfLines = 0
label.font = UIFont(name: "Avenir Next", size: 17)
label.textColor = UIColor.black
self.contentView.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
// Label constraints
let labelLeading = NSLayoutConstraint(item: label, attribute: .leading, relatedBy: .equal, toItem: contentView, attribute: .leading, multiplier: 1, constant: 20)
let labelTrailing = NSLayoutConstraint(item: label, attribute: .trailing, relatedBy: .equal, toItem: contentView, attribute: .trailing, multiplier: 1, constant: -20)
let labelTop = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: self.contentView.frame.origin.y)
self.contentView.addConstraints([labelLeading, labelTrailing, labelTop])
}
updateContentViewHeight()
}
}
}
catch{
print("html error\n",error)
}
}
func updateContentViewHeight(){
var totalContentHeight:CGFloat = 0.0
for i in self.contentView.subviews {
totalContentHeight += i.frame.height
}
let contentViewHeight:NSLayoutConstraint = NSLayoutConstraint(item: self.contentView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: totalContentHeight-29)
self.contentView.addConstraint(contentViewHeight)
}
I tried calling my stringFromHTML function in viewDidAppear. But it positions my UILabels to far far away (they don't seem).
I want to position my labels to the bottom of the last label, see the image below;
Anyone can help? Thanks in advance!
Change
let labelTop = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: self.contentView.frame.origin.y)
To
if label != nil {
let labelTop = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: label[index], attribute: .top, multiplier: 1, constant: 2)
} else {
let labelTop = NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: self.contentView.frame.origin.y)
}
Because you are basically forcing each label to be at the same y position.

How To Set Auto Layout Constraints Programmatically for UITextView For Universal App with swift

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

swift ios adding constraints for several subviews with a loop

I am newcomer in swift and tried to learn how to add several subviews and their constraints using a loop. I tried to follow some answer to similar questions but it is not working. Could you help me with my code? Thank you in advance.
Here is my code:
let card = UIView()
let view2 = UIView()
view2.backgroundColor = UIColor.greenColor()
view2.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view2)
let leftSideConstraint3 = NSLayoutConstraint(item: view2, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .Left, multiplier: 1, constant: 0.0)
let topConstraint3 = NSLayoutConstraint(item: view2, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0.0)
let widthConstraint3 = NSLayoutConstraint(item: view2, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: 1, constant: 0.0)
let heightConstraint3 = NSLayoutConstraint(item: view2, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: 1, constant: 0.0)
view.addConstraints([leftSideConstraint3, topConstraint3, heightConstraint3, widthConstraint3])
var cards = [UIView](count: 16, repeatedValue: card) // array with 16 cards
card.layer.borderWidth = 3
card.layer.borderColor = UIColor.blackColor().CGColor
card.backgroundColor = UIColor.orangeColor()
var columnCounter:Int = 0
var rowCounter:Int = 0
//Loop through each card in the array
for index in 0...cards.count-1 {
// place the card in the view and turn off translateAutoresizingMask
let thisCard = cards[index]
thisCard.translatesAutoresizingMaskIntoConstraints = false
view2.addSubview(thisCard)
//set the height and width constraints
let widthConstraint = NSLayoutConstraint(item: thisCard, attribute: .Width, relatedBy: .Equal, toItem: view2, attribute: .Width, multiplier: 0.25, constant: 0)
let heightConstraint = NSLayoutConstraint(item: thisCard, attribute: .Height, relatedBy: .Equal, toItem: view2, attribute: .Height, multiplier: 0.25, constant: 0)
view2.addConstraints([heightConstraint, widthConstraint])
//set the horizontal position
if (columnCounter > 0) {
// card is not in the first column
let cardOnTheLeft = cards[index-1]
let leftSideConstraint = NSLayoutConstraint(item: thisCard, attribute: .Left, relatedBy: .Equal, toItem: cardOnTheLeft, attribute: .Right, multiplier: 1, constant: 0)
//add constraint to the contentView
view2.addConstraint(leftSideConstraint)
} else {
//card is in the first column
let leftSideConstraint = NSLayoutConstraint(item: thisCard, attribute: .Left, relatedBy: .Equal, toItem: view2, attribute: .Left, multiplier: 1, constant: 0)
//add constraint to the contentView
view2.addConstraint(leftSideConstraint)
}
//set the vertical position
if (rowCounter > 0) {
// card is not in the first row
let cardOnTop = cards[index-4]
let topConstraint = NSLayoutConstraint(item: thisCard, attribute: .Top, relatedBy: .Equal, toItem: cardOnTop, attribute: .Bottom, multiplier: 1, constant: 0)
// add constraint to the contentView
view2.addConstraint(topConstraint)
} else {
//card is in the first row
let topConstraint = NSLayoutConstraint(item: thisCard, attribute: .Top, relatedBy: .Equal, toItem: view2, attribute: .Top, multiplier: 1, constant: 0)
//add constraint to the contentView
view2.addConstraint(topConstraint)
}
//increment the column counter
columnCounter = columnCounter+1
//if the column counter reaches the fifth column reset it and increase the row counter
if (columnCounter >= 4) {
columnCounter = 0
rowCounter = rowCounter+1
}
} // end of the loop
This creates an array with 16 references to the same card.
var cards = [UIView](count: 16, repeatedValue: card)
Replace it with:
var cards = (1...16).map { _ in UIView() }
which will result in an array with 16 unique UIViews.

Centering a UIView programmatically using Autolayout makes the view disappear from superview

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])
}

Resources