Swift How to resize an image inside a stackview - ios

I have 2 items in a stack view one is an image and the other is a label. I want the image to resize according to its frame size not the size of stack how can i change this.
//stack code
lazy var releaseInfoStack:UIStackView = {
let v = UIStackView()
v.translatesAutoresizingMaskIntoConstraints = false
v.spacing = 4
v.alignment = .center
v.axis = .horizontal
return v
}()
//my image
lazy var smallRealeaseImageIcon:UIImageView = {
let v = UIImageView(frame:CGRect(x: 0, y: 0, width: 17, height: 17))
v.contentMode = .scaleAspectFit
v.image = UIImage(named: "link")
return v
}()
//this is the current image below but i want the link icon to be smaller

You can add constraints for height and width:
let imageViewWidthConstraint = NSLayoutConstraint(item: smallRealeaseImageIcon, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let imageViewHeightConstraint = NSLayoutConstraint(item: smallRealeaseImageIcon, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
smallRealeaseImageIcon.addConstraints([imageViewWidthConstraint, imageViewHeightConstraint])
Just make sure to set the stackview's distribution to proportionally:
releaseInfoStack.distribution = .fillProportionally

Related

Navigation LeftBarButtonItem is not rendering properly , frame doesn't work

I'm following this tutorial..
Title view is working fine. Left bar item is not looking good. I have no idea why.
Here is my code..
func setUpNavigationBarItems(){
//https://www.youtube.com/watch?v=zS-CCd4xmRY
let titleImageView = UIImageView(image: UIImage(named: "ic_nav_app_icon"))
titleImageView.frame = CGRect(x: 0, y: 0, width: 34, height: 34)
titleImageView.contentMode = .scaleAspectFit
navigationItem.titleView = titleImageView
let addButton = UIButton(type: .system)
let addImage = UIImage(named: "ic_nav_add")
addButton.setImage(addImage?.withRenderingMode(.alwaysOriginal), for: .normal)
addButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: addButton)
}
The frame you are setting is being overruled by Auto Layout.
The same is probably true for the title view. If you take a look at its frame at runtime, it is likely not of size h34 w34. It just appears to be because you set titleImageView.contentMode = .scaleAspectFit, which will make the image fit inside its frame without stretching it.
The left button on the other hand appears distorted because you add the image to a UIButton which uses a UIImageView with contentMode = .scaleAspectFill to display the image (you can see this for yourself by using the view debugger and inspecting the navigation items).
To fix this, I suggest using Auto Layout to specify the size of the items in terms of constraints:
func setUpNavigationBarItems(){
let titleImageView = UIImageView(image: UIImage(named: "ic_nav_app_icon"))
NSLayoutConstraint(item: titleImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 34).isActive = true
NSLayoutConstraint(item: titleImageView, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 34).isActive = true
titleImageView.contentMode = .scaleAspectFit
navigationItem.titleView = titleImageView
let addButton = UIButton(type: .system)
let addImage = UIImage(named: "ic_nav_add")
addButton.setImage(addImage?.withRenderingMode(.alwaysOriginal), for: .normal)
NSLayoutConstraint(item: addButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
NSLayoutConstraint(item: addButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 30).isActive = true
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: addButton)
}

Programmatically create UIView with size based on subviews(labels)

I have a scenario where I'm needed to create an UIView completely programatically. This view is displayed as an overlay over a video feed and it contains 2 UILabels as subviews.
These labels can have varying widths and heights so I don't want to create a fixed frame for the UIView or the labels.
I've done this with programatic constrains without issue, but the problem is that the views are glitching when moving the phone and the framework that I'm using that does the video feed recommends not to use autolayout because of this exact problem.
My current code:
I create the view
func ar(_ arViewController: ARViewController, viewForAnnotation: ARAnnotation) -> ARAnnotationView {
let annotationView = AnnotationView() //*this is the view, inherits from UIView
annotationView.annotation = viewForAnnotation
annotationView.delegate = self
return annotationView
}
And the actual view code
class AnnotationView: ARAnnotationView {
var titleLabel: UILabel?
var distanceLabel: UILabel!
var delegate: AnnotationViewDelegate?
override var annotation: ARAnnotation? {
didSet {
loadUI()
}
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
setupUI()
}
private func setupUI() {
layer.cornerRadius = 5
layer.masksToBounds = true
backgroundColor = .transparentWhite
}
private func loadUI() {
titleLabel?.removeFromSuperview()
distanceLabel?.removeFromSuperview()
let label = UILabel()
label.font = UIFont(name: "AvenirNext-Medium", size: 16.0) ?? UIFont.systemFont(ofSize: 14)
label.numberOfLines = 2
label.textColor = UIColor.white
self.titleLabel = label
distanceLabel = UILabel()
distanceLabel.textColor = .cbPPGreen
distanceLabel.font = UIFont(name: "AvenirNext-Medium", size: 14.0) ?? UIFont.systemFont(ofSize: 12)
distanceLabel.textAlignment = .center
self.translatesAutoresizingMaskIntoConstraints = false
label.translatesAutoresizingMaskIntoConstraints = false
distanceLabel.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(label)
self.addSubview(distanceLabel)
if self.constraints.isEmpty {
NSLayoutConstraint.activate([
NSLayoutConstraint(item: label, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 5),
NSLayoutConstraint(item: label, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: -5),
NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: label, attribute: .bottom, relatedBy: .equal, toItem: distanceLabel, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: distanceLabel, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0),
NSLayoutConstraint(item: distanceLabel, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0),
NSLayoutConstraint(item: distanceLabel, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: distanceLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 15),
])
}
if let annotation = annotation as? BikeStationAR {
titleLabel?.text = annotation.address
distanceLabel?.text = String(format: "%.2f km", annotation.distanceFromUser / 1000)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
delegate?.didTouch(annotationView: self)
}
}
So my problem is now: how can I achieve the same behavior, without using autolayout?
Edit: here is a video of the behavior https://streamable.com/s/4zusq/dvbgnb
Edit 2: The solution is based on Mahgol Fa's answer and using a stack view or vertical positioning in order not to set the frame for the individual labels. So the final implementation is
private func loadUI() {
titleLabel?.removeFromSuperview()
distanceLabel?.removeFromSuperview()
let label = UILabel()
label.font = titleViewFont
label.numberOfLines = 0
label.textColor = UIColor.white
label.textAlignment = .center
self.titleLabel = label
distanceLabel = UILabel()
distanceLabel.textColor = .cbPPGreen
distanceLabel.font = subtitleViewFont
distanceLabel.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
distanceLabel.translatesAutoresizingMaskIntoConstraints = false
if let annotation = annotation as? BikeStationAR {
let title = annotation.address
let subtitle = String(format: "%.2f km", annotation.distanceFromUser / 1000)
let fontAttributeTitle = [NSAttributedString.Key.font: titleViewFont]
let titleSize = title.size(withAttributes: fontAttributeTitle)
let fontAttributeSubtitle = [NSAttributedString.Key.font: subtitleViewFont]
let subtitleSize = annotation.address.size(withAttributes: fontAttributeSubtitle)
titleLabel?.text = title
distanceLabel?.text = subtitle
let stackView = UIStackView(frame: CGRect(x: 0, y: 0, width: titleSize.width + 5, height: titleSize.height + subtitleSize.height + 5))
stackView.axis = .vertical
stackView.distribution = .fillProportionally
stackView.alignment = .fill
stackView.addArrangedSubview(titleLabel ?? UIView())
stackView.addArrangedSubview(distanceLabel)
frame = stackView.bounds
addSubview(stackView)
}
}
This way you can get the width of your lablels texts:
let fontAttributes1= [NSAttributedStringKey.font: your declared font in your code]
let size1 = (“your string” as String).size(withAttributes: fontAttributes1)
let fontAttributes2= [NSAttributedStringKey.font: your declared font in your code]
let size2 = (“your string” as String).size(withAttributes: fontAttributes2)
Then :
YourView.frame = CGRect( width: size1.width + size2.width + some spacing , height : ....)

Add UIImageView, UILabel to UIStackView programmatically in Swift 4

I'm trying to create a set of UIImageView, UILabel and add to UIStackView programmatically like so.
var someLetters: [Int: String] = [0:"g",1:"n",2:"d"]
let stackView1 = UIStackView(arrangedSubviews: createLetters(someLetters))
someScrollView.addSubview(stackView1)
func createLetters(_ named: [Int: String]) -> [UIView] {
return named.map { name in
let letterImage = UIImageView()
letterImage.image = UIImage(named: "\(name.key)")
let letterLabel = UILabel()
letterLabel.text = name.value
let subView = UIView()
subView.addSubview(letterLabel)
subView.addSubview(letterImage)
return subView
}
}
UIStackViews arrangedSubviews only accepts UIView as a parameter
so I created an additional UIView as a container of UILabel and UIImageView. No compile error but not seeing any UI elements on a screen.
What am I doing wrong here?
Add some constraints to
stackView1.alignment = .center
stackView1.distribution = .fillEqually
stackView1.axis = .vertical
stackView1.spacing = 10.0
stackView1.translatesAutoresizingMaskIntoConstraints = false
let leading = NSLayoutConstraint(item: stackView1, attribute: .leading, relatedBy: .equal, toItem: someScrollView, attribute: .leading, multiplier: 1, constant: 5)
let trailing = NSLayoutConstraint(item: stackView1, attribute: .trailing, relatedBy: .equal, toItem: someScrollView, attribute: .trailing, multiplier: 1, constant: 5)
let height = NSLayoutConstraint(item: stackView1, attribute: .height, relatedBy: .equal, toItem: someScrollView, attribute: .height, multiplier: 0.8, constant: 50)
let alignInCenter = NSLayoutConstraint(item: stackView1, attribute: .centerX, relatedBy: .equal, toItem: someScrollView, attribute: .centerX, multiplier: 1, constant: 1)
let alignInCenterY = NSLayoutConstraint(item: stackView1, attribute: .centerY, relatedBy: .equal, toItem: someScrollView, attribute: .centerY, multiplier: 1, constant: 1)
someScrollView.addSubview(stackView1)
NSLayoutConstraint.activate([alignInCenter,alignInCenterY,leading, trailing, height])
and also add some constraints to letters and ImageView
func createLetters(_ named: [Int: String]) -> [UIView] {
return named.map { name in
let letterImage = UIImageView()
letterImage.image = UIImage(named: "\(name.key)")
letterImage.backgroundColor = UIColor.gray
let letterLabel = UILabel()
letterLabel.text = name.value
letterLabel.backgroundColor = UIColor.green
let stackView = UIStackView(arrangedSubviews: [letterLabel, letterImage])
stackView.alignment = .center
stackView.distribution = .fillEqually
stackView.axis = .horizontal
let widht = NSLayoutConstraint(item: letterImage, attribute: NSLayoutAttribute.width, relatedBy: .equal, toItem: stackView, attribute: .width, multiplier: 1, constant: 100)
let height = NSLayoutConstraint(item: letterImage, attribute: NSLayoutAttribute.height, relatedBy: .equal, toItem: stackView, attribute: .height, multiplier: 1, constant: 100)
NSLayoutConstraint.activate([widht, height])
return stackView
}
}
But I am not sure what axis and what kind of distribution you are looking for in stackView. This code has some of the constraints missing so you have to identify and add them so there is no ambiguity in layouts
As a starting point, try adding the UILabels and UIImageViews to stack views in a storyboard. Here you can see I'm using horizontal stack views contained in a vertical stack view. From here you can see the constraints you'll need to use to create the same in code.
Update code as:
func createLetters(_ named: [Int: String]) -> [UIView] {
return named.map { name in
let letterImage = UIImageView(frame: CGRect(x: 0, y: name.key*10, width: 20, height: 20))
letterImage.image = UIImage(named: "(name.key)")
letterImage.backgroundColor = .green
let letterLabel = UILabel(frame: CGRect(x: 30, y: name.key*10, width: 20, height: 20))
letterLabel.text = name.value
letterLabel.backgroundColor = .red
let stackView = UIStackView(frame: CGRect(x: 0, y:name.key*30, width: 100, height: 50))
stackView.axis = UILayoutConstraintAxis.vertical
stackView.distribution = UIStackViewDistribution.equalSpacing
stackView.alignment = UIStackViewAlignment.center
stackView.spacing = 16.0
stackView.addArrangedSubview(letterImage)
stackView.addArrangedSubview(letterLabel)
return stackView
}
}
This will return 3 stack views as someLetters array count is 3. call it as
self.tview.addSubview(createLetters(someLetters)[0])
self.tview.addSubview(createLetters(someLetters)[1])
self.tview.addSubview(createLetters(someLetters)[2])
if you to get only one stack view label and image with same code then pass only one parameter.

How to make Constraints programmatically in iOS Swift 3.1? [duplicate]

I'm trying to figure this out since last week without going any step further. Ok, so I need to apply some constraints programmatically in Swift to a UIView using this code:
var new_view:UIView! = UIView(frame: CGRectMake(0, 0, 100, 100));
new_view.backgroundColor = UIColor.redColor();
view.addSubview(new_view);
var constX:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0);
self.view.addConstraint(constX);
var constY:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0);
self.view.addConstraint(constY);
var constW:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: new_view, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 0);
self.view.addConstraint(constW);
var constH:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: new_view, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 0);
self.view.addConstraint(constH);
But Xcode returns this weird output:
2014-10-03 09:48:12.657 Test[35088:2454916] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x7fa4ea446830 UIView:0x7fa4ea429290.centerX == UIView:0x7fa4ea4470f0.centerX>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea4516c0 h=--& v=--& UIView:0x7fa4ea429290.midX == + 50>",
"<NSLayoutConstraint:0x7fa4ea452830 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7fa4ea4470f0(375)]>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea446db0 h=-&- v=-&- 'UIView-Encapsulated-Layout-Left' H:|-(0)-[UIView:0x7fa4ea4470f0] (Names: '|':UIWindow:0x7fa4ea444b20 )>"
)
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fa4ea446830 UIView:0x7fa4ea429290.centerX == UIView:0x7fa4ea4470f0.centerX>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in
<UIKit/UIView.h> may also be helpful.
2014-10-03 09:48:12.658 Test[35088:2454916] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x7fa4ea44d160 UIView:0x7fa4ea429290.centerY == UIView:0x7fa4ea4470f0.centerY>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea451b30 h=--& v=--& UIView:0x7fa4ea429290.midY == + 50>",
"<NSLayoutConstraint:0x7fa4ea44cf00 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7fa4ea4470f0(667)]>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea452700 h=-&- v=-&- 'UIView-Encapsulated-Layout-Top' V:|-(0)-[UIView:0x7fa4ea4470f0] (Names: '|':UIWindow:0x7fa4ea444b20 )>"
)
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fa4ea44d160 UIView:0x7fa4ea429290.centerY == UIView:0x7fa4ea4470f0.centerY>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Can you help me?
Thanks a lot
Do you plan to have a squared UIView of width: 100 and Height: 100 centered inside the UIView of an UIViewController? If so, you may try one of the 6 following Auto Layout styles (Swift 5 / iOS 12.2):
1. Using NSLayoutConstraint initializer
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
}
2. Using Visual Format Language
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": view!, "newView": newView]
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterY, metrics: nil, views: views)
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterX, metrics: nil, views: views)
view.addConstraints(horizontalConstraints)
view.addConstraints(verticalConstraints)
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": view!, "newView": newView]
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterY, metrics: nil, views: views)
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterX, metrics: nil, views: views)
NSLayoutConstraint.activate(horizontalConstraints)
NSLayoutConstraint.activate(verticalConstraints)
}
3. Using a mix of NSLayoutConstraint initializer and Visual Format Language
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["newView": newView]
let widthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let heightConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
view.addConstraints(widthConstraints)
view.addConstraints(heightConstraints)
view.addConstraints([horizontalConstraint, verticalConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["newView": newView]
let widthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let heightConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
NSLayoutConstraint.activate(widthConstraints)
NSLayoutConstraint.activate(heightConstraints)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["newView": newView]
let widthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let heightConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
NSLayoutConstraint.activate(widthConstraints)
NSLayoutConstraint.activate(heightConstraints)
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0).isActive = true
}
4. Using UIView.AutoresizingMask
Note: Springs and Struts will be translated into corresponding auto layout constraints at runtime.
override func viewDidLoad() {
let newView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = true
newView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
newView.autoresizingMask = [UIView.AutoresizingMask.flexibleLeftMargin, UIView.AutoresizingMask.flexibleRightMargin, UIView.AutoresizingMask.flexibleTopMargin, UIView.AutoresizingMask.flexibleBottomMargin]
}
5. Using NSLayoutAnchor
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = newView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = newView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let widthConstraint = newView.widthAnchor.constraint(equalToConstant: 100)
let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 100)
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = newView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = newView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let widthConstraint = newView.widthAnchor.constraint(equalToConstant: 100)
let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 100)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
newView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
newView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newView.widthAnchor.constraint(equalToConstant: 100).isActive = true
newView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
6. Using intrinsicContentSize and NSLayoutAnchor
import UIKit
class CustomView: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: 100, height: 100)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
let newView = CustomView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = newView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = newView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
}
}
Result:
It helps me to learn visually, so this is a supplemental answer.
Boilerplate code
override func viewDidLoad() {
super.viewDidLoad()
let myView = UIView()
myView.backgroundColor = UIColor.blue
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
// Add constraints code here
// ...
}
Each of the following examples are independent of the others.
Pin left edge
myView.leading = leadingMargin + 20
Method 1: Anchor Style
let margins = view.layoutMarginsGuide
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 20).isActive = true
In addition to leadingAnchor, there is also trailingAnchor, topAnchor, and bottomAnchor.
Method 2: NSLayoutConstraint Style
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.leadingMargin, multiplier: 1.0, constant: 20.0).isActive = true
In addition to .leading there is also .trailing, .top, and .bottom.
In addition to .leadingMargin there is also .trailingMargin, .topMargin, and .bottomMargin.
Set Width and Height
width = 200
height = 100
Method 1: Anchor Style
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
Method 2: NSLayoutConstraint Style
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
Center in container
myView.centerX = centerX
myView.centerY = centerY
Method 1: Anchor Style
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
Method 2: NSLayoutConstraint Style
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true
Notes
Anchor style is the preferred method over NSLayoutConstraint Style, however it is only available from iOS 9, so if you are supporting iOS 8 then you should still use NSLayoutConstraint Style.
The examples above showed just the one or two constraints that were being focused on. However, in order to properly place myView in my test project I needed to have four constraints.
Further Reading
Programmatically Creating Constraints documentation
If you want to fill your super view then I suggest the swifty way:
view.translatesAutoresizingMaskIntoConstraints = false
let attributes: [NSLayoutAttribute] = [.top, .bottom, .right, .left]
NSLayoutConstraint.activate(attributes.map {
NSLayoutConstraint(item: view, attribute: $0, relatedBy: .equal, toItem: view.superview, attribute: $0, multiplier: 1, constant: 0)
})
Other wise if you need non equal constraints check out NSLayoutAnchor as of iOS 9. Its often much easier to read that using NSLayoutConstraint directly:
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: view.superview!.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: view.superview!.bottomAnchor).isActive = true
view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor, constant: 10).isActive = true
view.trailingAnchor.constraint(equalTo: view.superview!.trailingAnchor, constant: 10).isActive = true
We can easily do this with in swift 5.1
setup 1
subview align to view center
subview width height set using float
view.addSubview(myView1)
myView1.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myView1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myView1.centerYAnchor.constraint(equalTo: view.centerYAnchor),
myView1.widthAnchor.constraint(equalToConstant: 100),
myView1.heightAnchor.constraint(equalToConstant: 100),
])
setup 2
subview align to view leading and top anchor
subview width set using view width height
view.addSubview(myView2)
myView2.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myView2.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 16),
myView2.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,constant: 16),
myView2.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3),
myView2.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3)
])
Constraints for multiple views in playground.
swift 3+
var yellowView: UIView!
var redView: UIView!
override func loadView() {
// UI
let view = UIView()
view.backgroundColor = .white
yellowView = UIView()
yellowView.backgroundColor = .yellow
view.addSubview(yellowView)
redView = UIView()
redView.backgroundColor = .red
view.addSubview(redView)
// Layout
redView.translatesAutoresizingMaskIntoConstraints = false
yellowView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
yellowView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
yellowView.widthAnchor.constraint(equalToConstant: 80),
yellowView.heightAnchor.constraint(equalToConstant: 80),
redView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20),
redView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -20),
redView.widthAnchor.constraint(equalToConstant: 80),
redView.heightAnchor.constraint(equalToConstant: 80)
])
self.view = view
}
In my opinion xcode playground is the best place for learning adding
constraints programmatically.
Basically it involved 3 steps
fileprivate func setupName() {
lblName.text = "Hello world"
// Step 1
lblName.translatesAutoresizingMaskIntoConstraints = false
//Step 2
self.view.addSubview(lblName)
//Step 3
NSLayoutConstraint.activate([
lblName.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
lblName.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
])
}
This puts label "hello world" in center of screen.
Please refer link Autolayout constraints programmatically
The problem, as the error message suggests, is that you have constraints of type NSAutoresizingMaskLayoutConstraints that conflict with your explicit constraints, because new_view.translatesAutoresizingMaskIntoConstraints is set to true.
This is the default setting for views you create in code. You can turn it off like this:
var new_view:UIView! = UIView(frame: CGRectMake(0, 0, 100, 100))
new_view.translatesAutoresizingMaskIntoConstraints = false
Also, your width and height constraints are weird. If you want the view to have a constant width, this is the proper way:
new_view.addConstraint(NSLayoutConstraint(
item:new_view, attribute:NSLayoutAttribute.Width,
relatedBy:NSLayoutRelation.Equal,
toItem:nil, attribute:NSLayoutAttribute.NotAnAttribute,
multiplier:0, constant:100))
(Replace 100 by the width you want it to have.)
If your deployment target is iOS 9.0 or later, you can use this shorter code:
new_view.widthAnchor.constraintEqualToConstant(100).active = true
Anyway, for a layout like this (fixed size and centered in parent view), it would be simpler to use the autoresizing mask and let the system translate the mask into constraints:
var new_view:UIView! = UIView(frame: CGRectMake(0, 0, 100, 100))
new_view.backgroundColor = UIColor.redColor();
view.addSubview(new_view);
// This is the default setting but be explicit anyway...
new_view.translatesAutoresizingMaskIntoConstraints = true
new_view.autoresizingMask = [ .FlexibleTopMargin, .FlexibleBottomMargin,
.FlexibleLeftMargin, .FlexibleRightMargin ]
new_view.center = CGPointMake(view.bounds.midX, view.bounds.midY)
Note that using autoresizing is perfectly legitimate even when you're also using autolayout. (UIKit still uses autoresizing in lots of places internally.) The problem is that it's difficult to apply additional constraints to a view that is using autoresizing.
Updated for Swift 3
import UIKit
class ViewController: UIViewController {
let redView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupAutoLayout()
}
func setupViews() {
view.backgroundColor = .white
view.addSubview(redView)
}
func setupAutoLayout() {
// Available from iOS 9 commonly known as Anchoring System for AutoLayout...
redView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
redView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
redView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
redView.heightAnchor.constraint(equalToConstant: 300).isActive = true
// You can also modified above last two lines as follows by commenting above & uncommenting below lines...
// redView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
// redView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
Type of Constraints
/*
// regular use
1.leftAnchor
2.rightAnchor
3.topAnchor
// intermediate use
4.widthAnchor
5.heightAnchor
6.bottomAnchor
7.centerXAnchor
8.centerYAnchor
// rare use
9.leadingAnchor
10.trailingAnchor
etc. (note: very project to project)
*/
Auto layout is realized by applying constraints on images. Use NSLayoutConstraint. It is possible to implement an ideal and beautiful design on all devices. Please try the code below.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myImageView:UIImageView = UIImageView()
myImageView.backgroundColor = UIColor.red
myImageView.image = UIImage(named:"sample_dog")!
myImageView.translatesAutoresizingMaskIntoConstraints = false
myImageView.layer.borderColor = UIColor.red.cgColor
myImageView.layer.borderWidth = 10
self.view.addSubview(myImageView)
view.removeConstraints(view.constraints)
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .top,
relatedBy: .equal,
toItem: view,
attribute: .top,
multiplier: 1,
constant:100)
)
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1,
constant:0)
)
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .height,
relatedBy: .equal,
toItem: view,
attribute: .width,
multiplier: 0.5,
constant:40))
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .width,
relatedBy: .equal,
toItem: view,
attribute: .width,
multiplier: 0.5,
constant:40))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
the following code works for me in this scenario: an UIImageView forced landscape.
imagePreview!.isUserInteractionEnabled = true
imagePreview!.isExclusiveTouch = true
imagePreview!.contentMode = UIView.ContentMode.scaleAspectFit
// Remove all constraints
imagePreview!.removeAllConstraints()
// Add the new constraints
let guide = view.safeAreaLayoutGuide
imagePreview!.translatesAutoresizingMaskIntoConstraints = false
imagePreview!.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
imagePreview!.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
imagePreview!.heightAnchor.constraint(equalTo: guide.heightAnchor, multiplier: 1.0).isActive = true
where removeAllConstraints is an extension
extension UIView {
func removeAllConstraints() {
var _superview = self.superview
func removeAllConstraintsFromView(view: UIView) { for c in view.constraints { view.removeConstraint(c) } }
while let superview = _superview {
for constraint in superview.constraints {
if let first = constraint.firstItem as? UIView, first == self {
superview.removeConstraint(constraint)
}
if let second = constraint.secondItem as? UIView, second == self {
superview.removeConstraint(constraint)
}
}
_superview = superview.superview
}
self.removeConstraints(self.constraints)
self.translatesAutoresizingMaskIntoConstraints = true
}
}
If you find the above to be ugly. You should consider using a DSL for constraints. Such as SnapKit
Makes constraint API much more user-friendly
view.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
Would like to add some theoretical concept to Imanou Petit’s answer, so that one can understand how auto layout works.
To understand auto layout consider your view as rubber's object which is shrinked initially.
To place an object on screen we need 4 mandatory things :
X coordinate of object (horizontal position).
Y coordinate of object (vertical position )
Object’s Width
Object’s Height.
1 X coordinate: There are multiple ways of giving x coordinates to a view.
Such as Leading constraint, Trailing constraint , Horizontally centre
etc.
2 Y coordinate: There are multiple ways of giving y coordinates to a view :
Such as Top constraint, Bottom constraint , Vertical centre etc.
3 Object's width: There are two ways of giving width constrain to a view :
a. Add fixed width constraint (consider this constraint as iron rod of fixed width and you have hooked your rubber’s object horizontally with it so rubber’s object don’t shrink or expand)
b. Do not add any width constraint but add x coordinate constraint to both end of view trailing and leading, these two constraints will expand/shrink your rubber’s object by pulling/pushing it from both end, leading and trailing.
4 Object's height: Similar to width, there are two ways of giving height constraint to a view as well :
a. Add fixed height constraint (consider this constraints as iron rod of fixed height and you have hooked your rubber’s object vertically with it so rubber’s object don’t shrink or expand)
b. Do not add any height constraint but add x coordinate constraint to both end of view top and bottom, these two constraints will expand/shrink your rubber’s object pulling/pushing it from both end, top and bottom.
it is a little different in xcode 7.3.1. this is what i come up with
// creating the view
let newView = UIView()
newView.backgroundColor = UIColor.redColor()
newView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(newView)
// creating the constraint
// attribute and relation cannot be set directyl you need to create a cariable of them
let layout11 = NSLayoutAttribute.CenterX
let layout21 = NSLayoutRelation.Equal
let layout31 = NSLayoutAttribute.CenterY
let layout41 = NSLayoutAttribute.Width
let layout51 = NSLayoutAttribute.Height
let layout61 = NSLayoutAttribute.NotAnAttribute
// defining all the constraint
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: layout11, relatedBy: layout21, toItem: view, attribute: layout11, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: layout31, relatedBy: layout21, toItem: view, attribute: layout31, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: layout41, relatedBy: layout21, toItem: nil, attribute: layout61, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: layout51, relatedBy: layout21, toItem: nil, attribute: layout61, multiplier: 1, constant: 100)
// adding all the constraint
NSLayoutConstraint.activateConstraints([horizontalConstraint,verticalConstraint,widthConstraint,heightConstraint])
This is one way to adding constraints programmatically
override func viewDidLoad() {
super.viewDidLoad()
let myLabel = UILabel()
myLabel.labelFrameUpdate(label: myLabel, text: "Welcome User", font: UIFont(name: "times new roman", size: 40)!, textColor: UIColor.red, textAlignment: .center, numberOfLines: 0, borderWidth: 2.0, BorderColor: UIColor.red.cgColor)
self.view.addSubview(myLabel)
let myLabelhorizontalConstraint = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let myLabelverticalConstraint = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0)
let mylabelLeading = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.leading, multiplier: 1, constant: 10)
let mylabelTrailing = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: -10)
let myLabelheightConstraint = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)
NSLayoutConstraint.activate(\[myLabelhorizontalConstraint, myLabelverticalConstraint, myLabelheightConstraint,mylabelLeading,mylabelTrailing\])
}
extension UILabel
{
func labelFrameUpdate(label:UILabel,text:String = "This is sample Label",font:UIFont = UIFont(name: "times new roman", size: 20)!,textColor:UIColor = UIColor.red,textAlignment:NSTextAlignment = .center,numberOfLines:Int = 0,borderWidth:CGFloat = 2.0,BorderColor:CGColor = UIColor.red.cgColor){
label.translatesAutoresizingMaskIntoConstraints = false
label.text = text
label.font = font
label.textColor = textColor
label.textAlignment = textAlignment
label.numberOfLines = numberOfLines
label.layer.borderWidth = borderWidth
label.layer.borderColor = UIColor.red.cgColor
}
}
var xCenterConstraint : NSLayoutConstraint!
var yCenterConstraint: NSLayoutConstraint!
xCenterConstraint = NSLayoutConstraint(item: self.view, attribute: .CenterX, relatedBy: .Equal, toItem: (Your view NAme), attribute: .CenterX, multiplier: 1, constant: 0)
self.view.addConstraint(xCenterConstraint)
yCenterConstraint = NSLayoutConstraint(item: self.view, attribute: .CenterY, relatedBy: .Equal, toItem: (Your view Name), attribute: .CenterY, multiplier: 1, constant: 0)
self.view.addConstraint(yCenterConstraint)
Try this elegant UIView extension for constraints. You can do constraints easy as:
- firstView.coverWholeSuperview()
- firstView.constraints(size: CGSize(width: 44, height: 44), centerX: view.centerXAnchor, centerY: view.centerXAnchor)
- firstView.constraints(top: view.topAnchor,
leading: secondView.leadingAnchor,
bottom: view.bottomAnchor,
trailing: secondView.trailingAnchor,
padding: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12))
Here is extension, just copy it to your project.
extension UIView {
/// Attaches all sides of the receiver to its parent view
func coverWholeSuperview(margin: CGFloat = 0.0) {
let view = superview
layoutAttachTop(to: view, margin: margin)
layoutAttachBottom(to: view, margin: margin)
layoutAttachLeading(to: view, margin: margin)
layoutAttachTrailing(to: view, margin: margin)
}
/// Attaches the top of the current view to the given view's top if it's a superview of the current view
/// or to it's bottom if it's not (assuming this is then a sibling view).
#discardableResult
func layoutAttachTop(to: UIView? = nil, margin: CGFloat = 0.0) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = view == superview
let constraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .top : .bottom, multiplier: 1.0,
constant: margin)
superview?.addConstraint(constraint)
return constraint
}
/// Attaches the bottom of the current view to the given view
#discardableResult
func layoutAttachBottom(to: UIView? = nil, margin: CGFloat = 0.0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = (view == superview) || false
let constraint = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .bottom : .top, multiplier: 1.0,
constant: -margin)
if let priority = priority {
constraint.priority = priority
}
superview?.addConstraint(constraint)
return constraint
}
/// Attaches the leading edge of the current view to the given view
#discardableResult
func layoutAttachLeading(to: UIView? = nil, margin: CGFloat = 0.0) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = (view == superview) || false
let constraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .leading : .trailing, multiplier: 1.0,
constant: margin)
superview?.addConstraint(constraint)
return constraint
}
/// Attaches the trailing edge of the current view to the given view
#discardableResult
func layoutAttachTrailing(to: UIView? = nil, margin: CGFloat = 0.0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = (view == superview) || false
let constraint = NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .trailing : .leading, multiplier: 1.0,
constant: -margin)
if let priority = priority {
constraint.priority = priority
}
superview?.addConstraint(constraint)
return constraint
}
// For anchoring View
struct AnchoredConstraints {
var top, leading, bottom, trailing, width, height, centerX, centerY: NSLayoutConstraint?
}
#discardableResult
func constraints(top: NSLayoutYAxisAnchor? = nil, leading: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil,
trailing: NSLayoutXAxisAnchor? = nil, padding: UIEdgeInsets = .zero, size: CGSize = .zero,
centerX: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil,
centerXOffset: CGFloat = 0, centerYOffset: CGFloat = 0) -> AnchoredConstraints {
translatesAutoresizingMaskIntoConstraints = false
var anchoredConstraints = AnchoredConstraints()
if let top = top {
anchoredConstraints.top = topAnchor.constraint(equalTo: top, constant: padding.top)
}
if let leading = leading {
anchoredConstraints.leading = leadingAnchor.constraint(equalTo: leading, constant: padding.left)
}
if let bottom = bottom {
anchoredConstraints.bottom = bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom)
}
if let trailing = trailing {
anchoredConstraints.trailing = trailingAnchor.constraint(equalTo: trailing, constant: -padding.right)
}
if size.width != 0 {
anchoredConstraints.width = widthAnchor.constraint(equalToConstant: size.width)
}
if size.height != 0 {
anchoredConstraints.height = heightAnchor.constraint(equalToConstant: size.height)
}
if let centerX = centerX {
anchoredConstraints.centerX = centerXAnchor.constraint(equalTo: centerX, constant: centerXOffset)
}
if let centerY = centerY {
anchoredConstraints.centerY = centerYAnchor.constraint(equalTo: centerY, constant: centerYOffset)
}
[anchoredConstraints.top, anchoredConstraints.leading, anchoredConstraints.bottom,
anchoredConstraints.trailing, anchoredConstraints.width,
anchoredConstraints.height, anchoredConstraints.centerX,
anchoredConstraints.centerY].forEach { $0?.isActive = true }
return anchoredConstraints
}
}
You are adding all defined constraints to self.view which is wrong, as width and height constraint should be added to your newView.
Also, as I understand you want to set constant width and height 100:100.
In this case you should change your code to:
var constW = NSLayoutConstraint(item: newView,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 100)
newView.addConstraint(constW)
var constH = NSLayoutConstraint(item: newView,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 100)
newView.addConstraint(constH)
You can use Snapkit to set constraints programmatically.
class ViewController: UIViewController {
let rectView: UIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews() {
rectView.backgroundColor = .red
view.addSubview(rectView)
rectView.snp.makeConstraints {
$0.center.equalToSuperview()
}
}
}
The error is caused by constrains automatically created from autoresizing mask, they are created because UIView property translatesAutoresizingMaskIntoConstraints is true by default.
Consider using BoxView to get rid of all manual constraint creation boilerplate, and make your code concize and readable. To make layout in question with BoxView is very easy:
boxView.items = [
new_view.boxed.centerX().centerY().relativeWidth(1.0).relativeHeight(1.0)
]

Programmatically add full size view in swift

I'm working with a iMessage application and have programmatically added a view. However I can't seem to work out the correct constraints for making it the correct size at all times. For example, the view moves down a few hundred px if I leave the extension for another and come back to it. I think this has something to do with the .isActive. My goal is to make the view automatically resize to always be the right size or take up the full available height and width.
func createBrowser() {
let controller = MSStickerBrowserViewController(stickerSize: .small)
addChildViewController(controller)
view.addSubview(controller.view)
controller.view.translatesAutoresizingMaskIntoConstraints = false
controller.stickerBrowserView.backgroundColor = UIColor.blue
controller.stickerBrowserView.dataSource = self
view.topAnchor.constraint(equalTo: controller.view.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: controller.view.bottomAnchor).isActive = true
view.leftAnchor.constraint(equalTo: controller.view.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: controller.view.rightAnchor).isActive = true
view.centerXAnchor.constraint(equalTo: controller.view.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: controller.view.centerYAnchor).isActive = true
}
Screenshot: https://d17oy1vhnax1f7.cloudfront.net/items/1F2B0s3v0s1k3E2L0Z07/Screen%20Shot%202016-09-19%20at%2011.42.51%20AM.png
to better explain things I've put together the following. This demonstrates two methods of fixing the layout for subviews. When using constraints, I prefer to create the constraints as an array and activate them all in one go, as you will see in the code for createredSquareWithConstraints. A constraint is simply a linear equation relating the features of one view to that of another. In "pseudocode", for example, the first constraint in my array could be written:
"Set the leading margin of the subview equal to 1 times the leading margin of the container view plus a constant of 0."
(This is why I was getting confused earlier as it looked to me as though you were setting the containing view's constraints based on the characteristics of one of its subviews.)
While it remains perfectly valid to use layout constraints, I think the preferred methodology these days is to override the viewWillTransitionToSize() delegate method, which simply asks you to specify, given a size for the containing view, what the frame of a view controller's subviews should be. As such, I've included an implementation of this too, creating a yellow square with an initial frame that is then modified whenever viewWillTransitionToSize is called. I personally find this a lot less fiddly that using layout constraints.
If you lay around with the buttons and rotate the screen you should see that either method achieves the same thing. [NB I have labelled one square as constrained and one as unconstrained, but in reality they are of course both constrained, just in different ways. I would add that this is clearly not how you would do things in practice - you should choose one methodology and stick to it otherwise your code will be all over the place!].
Hope that helps!
import UIKit
class ViewController: UIViewController {
var constrainedredSquare : UIView!
var unconstrainedRedSquare : UIView!
var methodOneButton : UIButton!
var methodTwoButton : UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = UIColor.blue
func getButton(name: String) -> UIButton {
let button : UIButton = UIButton()
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 3
button.clipsToBounds = true
button.setTitle(name, for: UIControlState.normal)
button.setTitleColor(UIColor.black, for: UIControlState.normal)
return button
}
self.methodOneButton = getButton(name: "Red - Constraints")
self.methodTwoButton = getButton(name: "Yellow - viewWillTransitionToSize")
self.methodOneButton.addTarget(self, action: #selector(self.createRedSquareWithConstraints), for: .touchUpInside)
self.methodTwoButton.addTarget(self, action: #selector(self.createYellowSquareWithoutConstraints), for: .touchUpInside)
self.methodOneButton.frame = CGRect(origin: CGPoint(x: 200, y: 100), size: CGSize(width: 300, height: 300))
self.methodTwoButton.frame = CGRect(origin: CGPoint(x: self.view.frame.width - 500, y: 100), size: CGSize(width: 300, height: 300))
self.view.addSubview(self.methodOneButton)
self.view.addSubview(self.methodTwoButton)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if let _ = self.unconstrainedRedSquare {
self.unconstrainedRedSquare.frame = CGRect(origin: CGPoint.zero, size: size)
}
self.methodOneButton.frame = CGRect(origin: CGPoint(x: 200, y: 100), size: CGSize(width: 300, height: 300))
self.methodTwoButton.frame = CGRect(origin: CGPoint(x: size.width - 500, y: 100), size: CGSize(width: 300, height: 300))
}
func createYellowSquareWithoutConstraints() {
if let _ = self.unconstrainedRedSquare {
self.unconstrainedRedSquare.removeFromSuperview()
}
else
{
if let _ = constrainedredSquare {
self.constrainedredSquare.removeFromSuperview()
}
self.unconstrainedRedSquare = UIView()
self.unconstrainedRedSquare.backgroundColor = UIColor.yellow
self.unconstrainedRedSquare.frame = CGRect(origin: CGPoint.zero, size: self.view.frame.size)
self.view.addSubview(self.unconstrainedRedSquare)
self.view.bringSubview(toFront: self.methodOneButton)
self.view.bringSubview(toFront: self.methodTwoButton)
}
}
func createRedSquareWithConstraints() {
if let _ = self.constrainedredSquare {
self.constrainedredSquare.removeFromSuperview()
}
else
{
if let _ = self.unconstrainedRedSquare {
self.unconstrainedRedSquare.removeFromSuperview()
}
let redSquare : UIView = UIView()
redSquare.backgroundColor = UIColor.red
self.view.addSubview(redSquare)
self.view.bringSubview(toFront: self.methodOneButton)
self.view.bringSubview(toFront: self.methodTwoButton)
let rsConstraints : [NSLayoutConstraint] = [NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.leading, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.trailing, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.bottom, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.width, multiplier: 1.0, constant: 0),
NSLayoutConstraint(item: redSquare, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.height, multiplier: 1.0, constant: 0)]
redSquare.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(rsConstraints)
}
}
}
You can use my extension to UIView. It allows to add extra padding on any side (only if you want to):
public extension UIView {
typealias ConstraintsTupleStretched = (top:NSLayoutConstraint, bottom:NSLayoutConstraint, leading:NSLayoutConstraint, trailing:NSLayoutConstraint)
func addSubviewStretched(subview:UIView?, insets: UIEdgeInsets = UIEdgeInsets() ) -> ConstraintsTupleStretched? {
guard let subview = subview else {
return nil
}
subview.translatesAutoresizingMaskIntoConstraints = false
addSubview(subview)
let constraintLeading = NSLayoutConstraint(item: subview,
attribute: .Left,
relatedBy: .Equal,
toItem: self,
attribute: .Left,
multiplier: 1,
constant: insets.left)
addConstraint(constraintLeading)
let constraintTrailing = NSLayoutConstraint(item: self,
attribute: .Right,
relatedBy: .Equal,
toItem: subview,
attribute: .Right,
multiplier: 1,
constant: insets.right)
addConstraint(constraintTrailing)
let constraintTop = NSLayoutConstraint(item: subview,
attribute: .Top,
relatedBy: .Equal,
toItem: self,
attribute: .Top,
multiplier: 1,
constant: insets.top)
addConstraint(constraintTop)
let constraintBottom = NSLayoutConstraint(item: self,
attribute: .Bottom,
relatedBy: .Equal,
toItem: subview,
attribute: .Bottom,
multiplier: 1,
constant: insets.bottom)
addConstraint(constraintBottom)
return (constraintTop, constraintBottom, constraintLeading, constraintTrailing)
}
}
Usage:
view.addSubviewStretched(tableView)
let BorderedBackgroundInset = UIEdgeInsets(top: 1, left: 1, bottom: 1, right: 1)
view?.addSubviewStretched(calendar.view, insets: BorderedBackgroundInset)

Resources