3

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?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Putte
  • 526
  • 1
  • 6
  • 15
  • Have you tried using stack view ? – Teja Nandamuri Mar 09 '20 at 19:12
  • @TejaNandamuri Nop, but I’ve not code a lot using programmically constraints. So this is just a ”play-around” project to test and practice. But even if I use a stackview. How can I set them like I asked for?:) – Putte Mar 09 '20 at 19:13
  • Do you want "Hello" left-aligned; "TAP ME" right-aligned; and the pencil and switch next to each other, and centered horizontally? – DonMag Mar 09 '20 at 19:54
  • @DonMag Exactly! :) – Putte Mar 09 '20 at 19:58

4 Answers4

3

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

cristian_064
  • 576
  • 3
  • 16
2

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
        }
1

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)
Teja Nandamuri
  • 11,045
  • 6
  • 57
  • 109
  • I’ll check that out tomorrow. Ikr all of this, but not like ”programmically”. I’m used to do this using storyboard etc. But thanks so far!! – Putte Mar 09 '20 at 20:08
1

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


    }
}

****

M Ohamed Zead
  • 197
  • 10
  • As I replied to the others. I’ll definently try this tomorrow morning. Can you explain me what the ”multipler” does? Like ”1” and ”2”? I’m not used to code the constraints, i’m normally doing it by storybord but wanna extend my knowledge :) – Putte Mar 09 '20 at 20:43
  • ok , multipliers exists in storyboard too , if you have a view A and View B , you want width of A to be twice as Width of B so you just give multiplier of 2 to A width which means double B 's width or B can have multiplier of 0.5 to A width – M Ohamed Zead Mar 09 '20 at 20:49
  • here is another answer explaining it very good https://stackoverflow.com/questions/41216941/autolayout-understanding-multiplier – M Ohamed Zead Mar 09 '20 at 20:51