Where Should I Be Setting Autolayout Constraints When Creating Views Programmatically

Setting auto layout constraints programmatically on two views in ViewController

You are on the right track. The trick here is that you need to dynamically update the height of tableView to its contentSize.height so infoView will know it should either be vertically expanded or shrunk:

First you define a property that will hold the height constraint:

@property (nonatomic, strong) NSLayoutConstraint *tableViewHeightConstraint;

- (void)viewDidLoad {
...
self.tableViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self.tableView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0.0
constant:350.0];
}

Then you can update the height constraint right after tableView knows its contentSize (ex: viewDidAppear:animated):

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];

self.tableViewHeightConstraint.constant = self.tableView.contentSize.height;
[self.view layoutIfNeeded];
}

I created a sample project for you so you can play around with it to have a better understanding about what I intend to do.

Sample Image

swift how to set autolayout programmatically

There are various ways to do this. One approach:

  • declare two "constraint" arrays

    • one to hold the "narrow view" constraints
    • one to hold the "wide view" constraints
  • activate / deactivate the constraints as needed

Here is a complete example:

class ChangeLayoutViewController: UIViewController {

let redButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .red
v.setTitle("Red Button", for: [])
return v
}()

let blueButton: UIButton = {
let v = UIButton()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .blue
v.setTitle("Blue Button", for: [])
return v
}()

var narrowConstraints: [NSLayoutConstraint] = [NSLayoutConstraint]()
var wideConstraints: [NSLayoutConstraint] = [NSLayoutConstraint]()

override func viewDidLoad() {
super.viewDidLoad()

view.addSubview(redButton)
view.addSubview(blueButton)

let g = view.safeAreaLayoutGuide

var c: NSLayoutConstraint

// MARK: - narrow orientation

// constrain redButton above blueButton

// constrain redButton leading and trailing to safe-area (with 8-pts on each side)
c = redButton.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0)
narrowConstraints.append(c)
c = redButton.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0)
narrowConstraints.append(c)

// constrain blueButton leading and trailing to safe-area (with 8-pts on each side)
c = blueButton.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0)
narrowConstraints.append(c)
c = blueButton.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0)
narrowConstraints.append(c)

// constrain redButton top 40-pts from safe-area top
c = redButton.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0)
narrowConstraints.append(c)
// constrain blueButton top 20-pts from redButton bottom
c = blueButton.topAnchor.constraint(equalTo: redButton.bottomAnchor, constant: 20.0)
narrowConstraints.append(c)

// MARK: - wide orientation

// constrain redButton & blueButton side-by-side
// with equal widths and 8-pts between them

// constrain redButton leading 8-pts from safe-area leading
c = redButton.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0)
wideConstraints.append(c)
// constrain blueButton trailing 8-pts from safe-area trailing
c = blueButton.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0)
wideConstraints.append(c)

// constrain blueButton leading 8-pts from redButton trailing
c = blueButton.leadingAnchor.constraint(equalTo: redButton.trailingAnchor, constant: 8.0)
wideConstraints.append(c)

// constrain buttons to equal widths
c = blueButton.widthAnchor.constraint(equalTo: redButton.widthAnchor)
wideConstraints.append(c)

// constrain both buttons centerY to safe-area centerY
c = redButton.centerYAnchor.constraint(equalTo: g.centerYAnchor)
wideConstraints.append(c)
c = blueButton.centerYAnchor.constraint(equalTo: g.centerYAnchor)
wideConstraints.append(c)

// activate initial constraints based on view width:height ratio
changeConstraints(view.frame.width > view.frame.height)

}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
// change active set of constraints based on view width:height ratio
self.changeConstraints(size.width > size.height)
}

func changeConstraints(_ useWide: Bool) -> Void {
if useWide {
NSLayoutConstraint.deactivate(narrowConstraints)
NSLayoutConstraint.activate(wideConstraints)
} else {
NSLayoutConstraint.deactivate(wideConstraints)
NSLayoutConstraint.activate(narrowConstraints)
}
}

}

Results:

Sample Image

Sample Image

How to Create layout constraints programmatically

To fix a view to the bottom of the screen you need following constraints to set.

  1. Leading Constraint with respect of Parent View for - X
  2. Trailing Constraint with respect of Parent View for - Width
  3. Bottom Constraint with respect of Parent View for - Y
  4. Height Constraint attached to self for - Height.

Lets add.

UIView *subView=bottomView;
UIView *parent=self.view;

subView.translatesAutoresizingMaskIntoConstraints = NO;

//Trailing
NSLayoutConstraint *trailing =[NSLayoutConstraint
constraintWithItem:subView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:parent
attribute:NSLayoutAttributeTrailing
multiplier:1.0f
constant:0.f];

//Leading

NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:subView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:parent
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.f];

//Bottom
NSLayoutConstraint *bottom =[NSLayoutConstraint
constraintWithItem:subView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:parent
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.f];

//Height to be fixed for SubView same as AdHeight
NSLayoutConstraint *height = [NSLayoutConstraint
constraintWithItem:subView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0
constant:ADHeight];

//Add constraints to the Parent
[parent addConstraint:trailing];
[parent addConstraint:bottom];
[parent addConstraint:leading];

//Add height constraint to the subview, as subview owns it.
[subView addConstraint:height];

Hope this helps.

Cheers.

AutoLayout constraints for programmatically added buttons in a scrollView

I managed to make your code work with three major changes as follows:

1 - Your content view needs to be of type UIView

let view1 = UIView()

This view will hold a UIScrollView (view2) and they have to have their constraints set. On view1 you will be able to set the size of the scroll view, that's why I made view 2 with 0 distance from the super view.

view1.addSubview(view2)

var dictionary = [String: UIView]()
dictionary.updateValue(view1, forKey: "view1")
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view1]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view1]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])

dictionary = [String: UIView]()
dictionary.updateValue(view2, forKey: "view2")
view1.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view2]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])
view1.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view2]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])

2 - As you were using the same reference to create new buttons, the final array would have the same button in every position.
I moved the initialization of each button inside the for that creates buttons.

var button = UIButton.buttonWithType(UIButtonType.System) as! UIButton
view2.addSubview(button)

3- After that I just updated your constraints to be inserted between the buttons and the scroll view (view2).

And here is the final code:

import UIKit

class ViewController: UIViewController {

let sceneCount = 4
var buttons: [UIButton] = [UIButton]()

func makeLayout(){

//Make the content view
let view1 = UIView()
view1.setTranslatesAutoresizingMaskIntoConstraints(false)
view1.backgroundColor = UIColor.redColor()

//Make the scroll view
let view2 = UIScrollView()
view2.setTranslatesAutoresizingMaskIntoConstraints(false)
view2.backgroundColor = UIColor(red: 0.75, green: 0.75, blue: 0.1, alpha: 1.0)

view.addSubview(view1)
view1.addSubview(view2)

var dictionary = [String: UIView]()
dictionary.updateValue(view1, forKey: "view1")

view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view1]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view1]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])

dictionary = [String: UIView]()
dictionary.updateValue(view2, forKey: "view2")

view1.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[view2]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])
view1.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[view2]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary) as [AnyObject])

//Create the buttons
for i in 0...(sceneCount-1) {
var button = UIButton.buttonWithType(UIButtonType.System) as! UIButton

button.setTranslatesAutoresizingMaskIntoConstraints(false)
button.addTarget(self, action: Selector("scene\(i)Pressed"), forControlEvents: UIControlEvents.TouchUpInside)
button.setBackgroundImage(UIImage(named: "Scene\(i+1)"), forState: UIControlState.Normal)
button.sizeThatFits(CGSizeMake(40, 40))

buttons.append(button)
view2.addSubview(button)
}

//set vertical spacing for all buttons
for i in 0...(sceneCount-1) {
var button1 = buttons[i]
var dictionary2 = [String: UIButton]()
dictionary2.updateValue(button1, forKey: "button1")

view2.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[button1]-0-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary2) as [AnyObject])
}

//set horizontal distance to container for first button
if sceneCount > 0 {
var buttonFirst: UIButton = buttons[0]
var dictionary3 = [String: UIButton]()
dictionary3.updateValue(buttonFirst, forKey: "buttonFirst")
view2.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[buttonFirst]", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary3) as [AnyObject])
}

//set horizontal spacing between the buttons
for i in 1...(sceneCount-1) {
var button1 = buttons[i - 1]
var button2 = buttons[i]

var dictionary1 = [String: UIButton]()
dictionary1.updateValue(button1, forKey: "scene1")
dictionary1.updateValue(button2, forKey: "scene2")

view2.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[scene1]-[scene2]", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary1) as [AnyObject])
}

//set horizontal distance to container for last button
if sceneCount > 0 {
var buttonLast: UIButton = buttons[sceneCount-1]
var dictionary4 = [String: UIButton]()
dictionary4.updateValue(buttonLast, forKey: "buttonLast")
view2.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:[buttonLast]-|", options: NSLayoutFormatOptions(0), metrics: nil, views: dictionary4) as [AnyObject])
}

}

override func viewDidLoad() {
super.viewDidLoad()
makeLayout()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}

How to update the constant height constraint of a UIView programmatically?

Select the height constraint from the Interface builder and take an outlet of it. So, when you want to change the height of the view you can use the below code.

yourHeightConstraintOutlet.constant = someValue
yourView.layoutIfNeeded()

Method updateConstraints() is an instance method of UIView. It is helpful when you are setting the constraints programmatically. It updates constraints for the view. For more detail click here.

Programmatically creating constraints bound to view controller margins

Actually the problem in your code is that you did not set the translatesAutoresizingMaskIntoConstraints of myview to false, whenever you want to use auto-layout constraints then you have to set translatesAutoresizingMaskIntoConstraints of a view to false.
Another Problem is that you do not add myview on self.view I have updated your code and Its working fine According your constraints.

Put below code in your ViewController .

let myView = UIView()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false

view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Bottom, relatedBy: .Equal, toItem: self.bottomLayoutGuide, attribute:.Top, multiplier: 1, constant: 20))

view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 300))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .TrailingMargin, relatedBy: .Equal, toItem: view, attribute: .TrailingMargin, multiplier: 1, constant: 0))


Related Topics



Leave a reply



Submit