Constraints in swift

Viewed 129

I'm currently practicing coding everything on my apps. So I'm not using the storyboard at all.

I've a little question which I found hard to find.

Imagine I wanna display a tableview cell like this. This one includes:

  • Text (Hello)
  • An image (the pencil)
  • A switch (the switch)
  • A button (TAP ME!)

like this

So let's say the middle part. What is the approach to place two elements next to each other like I've done in the middle using constraints?

At the moment I've built it like this:

fileprivate func setTextConstraints() {
    textToDisplay.translatesAutoresizingMaskIntoConstraints = false
    textToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    textToDisplay.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
}

fileprivate func setButtonConstraints(){
    buttonToDisplay.translatesAutoresizingMaskIntoConstraints = false
    buttonToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    buttonToDisplay.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
}

fileprivate func setImageConstraints(){
    imageToDisplay.translatesAutoresizingMaskIntoConstraints = false
    imageToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    imageToDisplay.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
}

fileprivate func setSwitchConstraints(){
    switchToDisplay.translatesAutoresizingMaskIntoConstraints = false
    switchToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
    switchToDisplay.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -150).isActive = true //<-------
}

I assume the "-150" is a very bad practice. So I'm looking for a good way to place two items in the center and next to each other like I've done, but in a good way.

Should I place both elements in centerXAnchor and use constant for negative and positive values? Or how else can I accomplish this in the "best" way?

4 Answers

for your case I believe you should use UISTACKVIEW like this code, you can take advantage of the properties of the StackView.

import UIKit

class ViewController: UIViewController {

    lazy var label : UILabel = {
        let label = UILabel()
        label.text = "Hello"
        label.backgroundColor = .red

        return label
    }()
    lazy var imageView : UIImageView = {
        let imageView = UIImageView()
        imageView.image = UIImage(named: "your image")
        imageView.backgroundColor = .blue
        return imageView
    }()
    lazy var mySwicth : UISwitch = {
        let swi = UISwitch()
        return swi
    }()
    lazy var myButton : UIButton = {
        let button = UIButton(type: .custom)
        button.setTitle("Tapped", for: .normal)
        button.backgroundColor = .black
        return button
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupView()
    }
    func setupView(){

        let stackView = UIStackView(arrangedSubviews: [label,imageView,mySwicth,myButton])
        stackView.axis = .horizontal
        stackView.spacing = 10
        stackView.distribution = .fillEqually
        self.view.addSubview(stackView)
        stackView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: self.view.topAnchor,constant: 100),
            stackView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,constant: 10),
            stackView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10),
            stackView.heightAnchor.constraint(equalToConstant: 50)

        ])
    }


}

enter image description here

I would do something like this

 NSLayoutConstraint.activate([

    textToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor),
    textToDisplay.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),

    buttonToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor),
    buttonToDisplay.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),

    imageToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor),
    imageToDisplay.trailingAnchor.constraint(equalTo: centerXAnchor),

    switchToDisplay.centerYAnchor.constraint(equalTo: centerYAnchor),
    switchToDisplay.leadingAnchor.constraint(equalTo: centerXAnchor)

])

And just a method setConstraints for the anchors and you can do for the translatesAutoresizingMaskIntoConstraints

   self.view.subviews.forEach {
            $0.translatesAutoresizingMaskIntoConstraints = false
        }

You could use stack view as suggested by the user luffy.

To give you a basic understanding,

Imagine your horizontal layout for an individual element in the image you posted should be like this

| - [Leading] - Label - [Trailing] - |

| - [Leading] - Pencil - [Trailing] - |

| - [Leading] - Switch - [Trailing] - |

| - [Leading] - Button - [Trailing] - |

I broke this into 4 pieces. Ideally they should appear in one single line.

  1. You need to connect Label leading to its superview.
  2. Connect Pencil leading to Label trailing
  3. Connect Switch LEading to Pencil trailing
  4. Connect Button leading to switch trailing
  5. Connect Button trailing to its superview.

If you have a specific width requirement, you can also add a width constraint for that element .

For images, it is advisable to give width and height constraints since the imageview will resize based on the image size.

I suggest you to make use of the useful gist

Using that gist, your code will look something like:

    label.layout.pinLeadingToSuperview(constant: 10)
    label.layout.pinTopToSuperview(constant: 0)

    pencil.layout.pinLeadingToView(view: label, constant: 10)
    pencil.layout.width(30)
    pencil.layout.height(30)
    pencil.layout.pinTopToSuperview(constant: 0)

    tapMe.layout.pinTrailingToSuperview(constant: 0)
    tapMe.layout.pinTopToSuperview(constant: 0)
    tapMe.layout.pinLeadingToView(view: toggleSwitch, constant: 10)


    toggleSwitch.layout.pinTopToSuperview(constant: 0)
    toggleSwitch.layout.pinLeadingToView(view: pencil, constant: 10)

Using the linked gist, the code equivalent for stack view approach suggested by user luffy would be :

    stackView.layout.height(50)
    stackView.layout.pinTopToSuperview(constant: 100)
    stackView.layout.pinLeadingToSuperview(constant: 10)
    stackView.layout.pinTrailingToSuperview(constant: -10)

here is how to you can achieve that by using UIStackView very handy and easy to arrange components

class ComponentsTableViewCell: UITableViewCell {



    lazy var textToDisplay : UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "Hello"
        return label
    }()


    lazy var buttonToDisplay : UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle("TapMe", for: .normal)
        button.backgroundColor = .black
        return button
    }()


    lazy var imageToDisplay : UIImageView = {
        let image = UIImageView()
        image.translatesAutoresizingMaskIntoConstraints = false
        image.backgroundColor = .red
        return image
    }()



    lazy var switchToDisplay : UISwitch = {
        let sswitch = UISwitch()
        sswitch.translatesAutoresizingMaskIntoConstraints = false
        return sswitch
    }()



    lazy var stack : UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .horizontal
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()

    init() {
        super.init(style: .default, reuseIdentifier: "")
        addSubview(textToDisplay)
        addSubview(imageToDisplay)
        addSubview(buttonToDisplay)
        addSubview(switchToDisplay)
        setConstraints()
        setStackViewConstraints()

    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }


    fileprivate func setConstraints(){
        NSLayoutConstraint.activate([
            switchToDisplay.widthAnchor.constraint(equalToConstant: 80),


            textToDisplay.widthAnchor.constraint(equalTo: switchToDisplay.widthAnchor, multiplier: 2),

            buttonToDisplay.widthAnchor.constraint(equalTo: switchToDisplay.widthAnchor, multiplier: 1),

            imageToDisplay.widthAnchor.constraint(equalToConstant: 30)
        ])

    }







    fileprivate func setStackViewConstraints(){
        addSubview(stack)
        stack.addArrangedSubview(textToDisplay)
        stack.addArrangedSubview(imageToDisplay)
        stack.addArrangedSubview(switchToDisplay)
        stack.addArrangedSubview(buttonToDisplay)

        NSLayoutConstraint.activate([
            stack.leadingAnchor.constraint(equalTo: leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: trailingAnchor),
            stack.heightAnchor.constraint(equalToConstant: 50),
            stack.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])


    }
}

****

Related