aaK
aaK

Reputation: 103

Set the button with Autolayout through Programming

There are 6 buttons on the screen. I want to place 6 buttons on the screen, one after another, with the same distance as shown in the image in all the screen i.e 4S, 5S, 6, and 6 Plus through coding.

enter image description here

The constrain which we added and addConstraintToControls() calling in viewWillAppear:

func addConstraintToControls()
{
    self.twitterButton.translatesAutoresizingMaskIntoConstraints = false;
    self.facebookButton.translatesAutoresizingMaskIntoConstraints = false;
    self.googleButton.translatesAutoresizingMaskIntoConstraints = false;

    //Horizontal constraints:
    scrollView.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Leading, relatedBy: .Equal, toItem: scrollView, attribute: .Leading, multiplier: 1, constant:5))
    scrollView.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Leading, relatedBy: .Equal, toItem: twitterButton, attribute: .Trailing, multiplier: 1, constant:5))
    scrollView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Leading, relatedBy: .Equal, toItem: facebookButton, attribute: .Trailing, multiplier: 1, constant:5))

    scrollView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Trailing, relatedBy: .Equal, toItem: scrollView, attribute: .Trailing, multiplier: 1, constant:-5))
    scrollView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: facebookButton, attribute: .Width, multiplier: 1, constant:0))
    scrollView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: twitterButton, attribute: .Width, multiplier: 1, constant:0))

    //Ratio constraints:
    twitterButton.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Width, relatedBy: .Equal, toItem: twitterButton, attribute: .Height, multiplier: 1, constant:0))
    googleButton.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: googleButton, attribute: .Height, multiplier: 1, constant:0))
    facebookButton.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Width, relatedBy: .Equal, toItem: facebookButton, attribute: .Height, multiplier: 1, constant:0))

    //Top constraints:
    scrollView.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1, constant:5))
    scrollView.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1, constant:5))
    scrollView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Top, relatedBy: .Equal, toItem: scrollView, attribute: .Top, multiplier: 1, constant:5))
}

But we are not able to achieve button 2 and button 3 with the one after another button. I want to achieve this by coding+Swift and by using Autolayout and constraint.

I'm getting the warning below in the console:

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. 
(
    "<NSLayoutConstraint:0x7fe321ce1880 H:|-(5)-[MyApp.HKBadgeButton:0x7fe321dedae0]   (Names: '|':UIScrollView:0x7fe322070200 )>",
    "<NSIBPrototypingLayoutConstraint:0x7fe321dab140 'IB auto generated at build time for view with fixed frame' H:|-(145)-[MyApp.HKBadgeButton:0x7fe321dedae0](LTR)   (Names: '|':UIScrollView:0x7fe322070200 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fe321ce1880 H:|-(5)-[MyApp.HKBadgeButton:0x7fe321dedae0]   (Names: '|':UIScrollView:0x7fe322070200 )>

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.

Please advise.

Upvotes: 0

Views: 119

Answers (4)

Premkumar
Premkumar

Reputation: 239

/**** Swift 3.0 - Autolayout ****/

let label1 = UILabel()
    label1.translatesAutoresizingMaskIntoConstraints = false
    label1.backgroundColor = UIColor.red
    label1.text = "THESE"

    let label2 = UILabel()
    label2.translatesAutoresizingMaskIntoConstraints = false
    label2.backgroundColor = UIColor.cyan
    label2.text = "ARE"

    let label3 = UILabel()
    label3.translatesAutoresizingMaskIntoConstraints = false
    label3.backgroundColor = UIColor.yellow
    label3.text = "SOME"

    view.addSubview(label1)
    view.addSubview(label2)
    view.addSubview(label3)

    let viewsDictionary = ["label1": label1, "label2": label2, "label3": label3]
    let height: Int = 30
    let Y:Int = 100
    let X:Int = 10
    let space:Int = 10

    for label in viewsDictionary.keys {
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-\(Y)-[\(label)(\(height))]|", options: [], metrics: nil, views: viewsDictionary))
    }
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat:"H:|-\(X)-[label1]-\(space)-[label2(==label1)]-\(space)-[label3(==label1)]-\(space)-|", options: [], metrics: nil, views: viewsDictionary))

Upvotes: 0

ullstrm
ullstrm

Reputation: 10160

If you are to use constraints in code, I'd highly recommend using a framework for that. The apple-way of adding constraints is very messy.

A good framework for programmatic constraints is PureLayout. https://github.com/PureLayout/PureLayout

Hopefully this could help you sort out the errors.

Upvotes: 1

Yury
Yury

Reputation: 6114

You are using .Leading attribute everywhere! Use .Trailing sometimes. You may use implicit class dot notation to make your code shorter and more readable. Usually you managing views and constraints together in xib, or together in code, rarely separated. Because it is hard to guess how you construct your scrollView and contentView, let me show you how it can be done all in code, you can easy use parts of it:

let scrollView = UIScrollView()
let contentView = UIView()

var twitterButton: UIButton!
var facebookButton: UIButton!
var googleButton: UIButton!
var checkButton: UIButton!
var mailButton: UIButton!
var redditButton: UIButton!

First you need to setup contentView correctly. Let me set it up as 'table view' style, only vertical scroll. All following code should be written in viewDidLoad:

initializeAllButtons() // initialize all buttons first

view.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0))

scrollView.addSubview(contentView)
contentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Leading, relatedBy: .Equal, toItem: contentView, attribute: .Leading, multiplier: 1, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Trailing, relatedBy: .Equal, toItem: contentView, attribute: .Trailing, multiplier: 1, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant: 0))

// next line prevent scrollView from horizontal scroll
view.addConstraint(NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: contentView, attribute: .Width, multiplier: 1, constant: 0))

Now you need to add your (initialized!) buttons to contentView and add constraints:

contentView.addSubview(twitterButton)
contentView.addSubview(facebookButton)
contentView.addSubview(googleButton)
contentView.addSubview(checkButton)
contentView.addSubview(mailButton)
contentView.addSubview(redditButton)
twitterButton.translatesAutoresizingMaskIntoConstraints = false
facebookButton.translatesAutoresizingMaskIntoConstraints = false
googleButton.translatesAutoresizingMaskIntoConstraints = false
checkButton.translatesAutoresizingMaskIntoConstraints = false
mailButton.translatesAutoresizingMaskIntoConstraints = false
redditButton.translatesAutoresizingMaskIntoConstraints = false

Horizontal constraints (from your posted):

contentView.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Leading, relatedBy: .Equal, toItem: contentView, attribute: .Leading, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Leading, relatedBy: .Equal, toItem: twitterButton, attribute: .Trailing, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Leading, relatedBy: .Equal, toItem: facebookButton, attribute: .Trailing, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: checkButton, attribute: .Leading, relatedBy: .Equal, toItem: googleButton, attribute: .Trailing, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: mailButton, attribute: .Leading, relatedBy: .Equal, toItem: checkButton, attribute: .Trailing, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: redditButton, attribute: .Leading, relatedBy: .Equal, toItem: mailButton, attribute: .Trailing, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: contentView, attribute: .Trailing, relatedBy: .Equal, toItem: redditButton, attribute: .Trailing, multiplier: 1, constant:5))

Horizontal pictures equal size constraints:

contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: facebookButton, attribute: .Width, multiplier: 1, constant:0))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: twitterButton, attribute: .Width, multiplier: 1, constant:0))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: checkButton, attribute: .Width, multiplier: 1, constant:0))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: mailButton, attribute: .Width, multiplier: 1, constant:0))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: redditButton, attribute: .Width, multiplier: 1, constant:0))

Ratio constraints:

twitterButton.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Width, relatedBy: .Equal, toItem: twitterButton, attribute: .Height, multiplier: 1, constant:0))
googleButton.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Width, relatedBy: .Equal, toItem: googleButton, attribute: .Height, multiplier: 1, constant:0))
facebookButton.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Width, relatedBy: .Equal, toItem: facebookButton, attribute: .Height, multiplier: 1, constant:0))
checkButton.addConstraint(NSLayoutConstraint(item: checkButton, attribute: .Width, relatedBy: .Equal, toItem: checkButton, attribute: .Height, multiplier: 1, constant:0))
mailButton.addConstraint(NSLayoutConstraint(item: mailButton, attribute: .Width, relatedBy: .Equal, toItem: mailButton, attribute: .Height, multiplier: 1, constant:0))
redditButton.addConstraint(NSLayoutConstraint(item: redditButton, attribute: .Width, relatedBy: .Equal, toItem: redditButton, attribute: .Height, multiplier: 1, constant:0))

Top constraints:

contentView.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: checkButton, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: mailButton, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant:5))
contentView.addConstraint(NSLayoutConstraint(item: redditButton, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant:5))

Bottom constraints:

contentView.addConstraint(NSLayoutConstraint(item: twitterButton, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: . Bottom, multiplier: 1, constant:-5))
contentView.addConstraint(NSLayoutConstraint(item: facebookButton, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant:-5))
contentView.addConstraint(NSLayoutConstraint(item: googleButton, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant:-5))
contentView.addConstraint(NSLayoutConstraint(item: checkButton, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant:-5))
contentView.addConstraint(NSLayoutConstraint(item: mailButton, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant:-5))
contentView.addConstraint(NSLayoutConstraint(item: redditButton, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant:-5))

Upvotes: -1

James P
James P

Reputation: 4836

I think using autolayout visual format will massively reduce your code. If you put all the buttons in a container view you can set the width of the container so everything appears the size you want. Then add this container to your scrollView.

let views = ["view1":view1,
             "view2":view2,
             "view3":view3,
             "view4":view4,
             "view5":view5,
             "view6":view6]
let metrics = ["spacing":5]

containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[view1]-spacing-[view2(==view1)]-spacing-[view3(==view1)]-spacing-[view4(==view1)]-spacing-[view5(==view1)]-spacing-[view6(==view1)]|", options: [.AlignAllTop, .AlignAllBottom], metrics: metrics, views: views))
containerView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view1]|", options: [], metrics: metrics, views: views))

Upvotes: 1

Related Questions