jmlipman
jmlipman

Reputation: 172

Unable to properly arrange two UILabels in a Stackview in Swift programatically

This is the hierarchy I have:

  1. Vertical Stackview that will contain the rows (mystack)
  2. View, wrapper (parentview)
  3. Horizontal Stackview that will contain two labels (stacktmp)
  4. Labels x2 (tx, tx2)

Additionally: mystack has 4 constraints: leading, trailing, top and bottom are the same as "Safe view".

The code:

let parentview = UIView()
parentview.backgroundColor = UIColor.cyan
let stacktmp = UIStackView()
stacktmp.axis = .horizontal

let tx = UILabel()
tx.text = "ss"
tx.backgroundColor = UIColor.red
tx.font = UIFont.boldSystemFont(ofSize: 19)

let tx2 = UILabel()
tx2.text = "This is a long sentence to check whether the view and the stackview adapt successfully to the length of this label."
//tx2.numberOfLines = 0
stacktmp.addArrangedSubview(tx)
stacktmp.addArrangedSubview(tx2)

parentview.addSubview(stacktmp)

stacktmp.translatesAutoresizingMaskIntoConstraints = false
print(parentview.frame.size.height)
stacktmp.topAnchor.constraint(equalTo: parentview.topAnchor).isActive = true
stacktmp.heightAnchor.constraint(equalTo:    parentview.heightAnchor).isActive = true

mystack.addArrangedSubview(parentview)

stacktmp.trailingAnchor.constraint(equalTo: parentview.trailingAnchor, constant: 0).isActive = true
stacktmp.leadingAnchor.constraint(equalTo: parentview.leadingAnchor, constant: 0).isActive = true

What I get:

enter image description here

My goal:

I want the sentence of the second label to break the words and fill it below in more than one line if necessary.

I tried adding the following line: tx2.numberOfLines = 0 (commented in the code)

As a result I got:

enter image description here

It certainly splited the sentence as I wanted but now the first label expanded unnecessarily.

How could a keep the first label as narrow as possible while allowing the second label to use more than a single line if necessary? I've been trying to fix this for several hours already trying to change the constraints but I'm still unable to fix it.

Upvotes: 2

Views: 769

Answers (2)

DonMag
DonMag

Reputation: 77690

As was commented, Hugging and Compression Resistance Priorities are helpful (needed).

This should get you what you want:

override func viewDidLoad() {
    super.viewDidLoad()

    // create parentview
    let parentview = UIView()
    parentview.translatesAutoresizingMaskIntoConstraints = false
    parentview.backgroundColor = UIColor.cyan

    // add parentview to view
    view.addSubview(parentview)

    // constrain parentview to Zero all the way around
    parentview.topAnchor.constraint(equalTo: view.topAnchor, constant: 0.0).isActive = true
    parentview.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0).isActive = true
    parentview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0.0).isActive = true
    parentview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0.0).isActive = true

    // create stack view
    let stacktmp = UIStackView()
    stacktmp.translatesAutoresizingMaskIntoConstraints = false
    stacktmp.axis = .horizontal

    // add stack view to parentview
    parentview.addSubview(stacktmp)

    // constrain stack view top / leading / trailing at Zero, height equal to parentview height
    stacktmp.topAnchor.constraint(equalTo: parentview.topAnchor, constant: 0.0).isActive = true
    stacktmp.leadingAnchor.constraint(equalTo: parentview.leadingAnchor, constant: 0.0).isActive = true
    stacktmp.trailingAnchor.constraint(equalTo: parentview.trailingAnchor, constant: 0.0).isActive = true
    stacktmp.heightAnchor.constraint(equalTo: parentview.heightAnchor).isActive = true

    // create "left" label
    let tx = UILabel()
    tx.text = "ss"
    tx.backgroundColor = UIColor.red
    tx.font = UIFont.boldSystemFont(ofSize: 19)

    // set Content Hugging to 252
    tx.setContentHuggingPriority(UILayoutPriority(rawValue: 252), for: .horizontal)
    // set Content Compression Resistance to 1000
    tx.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000), for: .horizontal)

    // create "right" label
    let tx2 = UILabel()
    tx2.text = "This is a long sentence to check whether the view and the stackview adapt successfully to the length of this label."
    // Zero lines, to get word wrapping
    tx2.numberOfLines = 0

    // set Content Hugging to 252
    tx2.setContentHuggingPriority(UILayoutPriority(rawValue: 252), for: .horizontal)
    // set Content Compression Resistance to 1000
    tx2.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000), for: .horizontal)

    // add labels to stack view
    stacktmp.addArrangedSubview(tx)
    stacktmp.addArrangedSubview(tx2)

}

Result:

enter image description here

Upvotes: 3

iOS Geek
iOS Geek

Reputation: 4855

Try this

import UIKit

class stackQueryVC: UIViewController {

    /// Width Height
    let height = UIScreen.main.bounds.height
    let width = UIScreen.main.bounds.width

    private var mainStackView: UIStackView?
    private var internalView1: UIView?
    private var internalView2: UIView?
    private var label1 : UILabel?
    private var label2 : UILabel?

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        DispatchQueue.main.async {
            self.addStackView()
        }
    }

    func addStackView(){
        mainStackView = UIStackView()
        internalView1 = UIView()
        internalView2 = UIView()
        label1 = UILabel()
        label2 = UILabel()

        label1?.text = "ios-geek"
        label2?.text = "qwerykgasgdasgdhgashdgjasgdasgdjahsdjahsgdasdjagsjdahsgdjsdhasd"
        label1?.numberOfLines = 0
        label2?.numberOfLines = 0
        label1?.textColor = UIColor.black
        label2?.textColor = UIColor.black

        /// Set Translation Properties
        mainStackView?.translatesAutoresizingMaskIntoConstraints = false
        internalView1?.translatesAutoresizingMaskIntoConstraints = false
        internalView2?.translatesAutoresizingMaskIntoConstraints = false
        label1?.translatesAutoresizingMaskIntoConstraints = false
        label2?.translatesAutoresizingMaskIntoConstraints = false

        addContentView()
    }

    func addContentView(){

        /// Add Stack View Constraints
        mainStackView?.backgroundColor = UIColor.white
        self.view.addSubview(mainStackView!)
        mainStackView?.topAnchor.constraint(equalTo: self.view.topAnchor).isActive=true
        mainStackView?.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive=true
        mainStackView?.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive=true
        mainStackView?.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive=true

        /// Add First StackView
        mainStackView?.distribution = .fillEqually
        mainStackView?.axis = .horizontal
        mainStackView?.alignment = .center
        mainStackView?.spacing = 10
        mainStackView?.backgroundColor = UIColor.white

        /// View Properties
        internalView1?.backgroundColor = .red
        internalView2?.backgroundColor = .purple

        /// Add Views
        mainStackView?.addArrangedSubview(internalView1!)
        internalView1?.topAnchor.constraint(equalTo: mainStackView!.topAnchor).isActive=true
        internalView1?.bottomAnchor.constraint(equalTo: mainStackView!.bottomAnchor).isActive=true
        internalView1?.leadingAnchor.constraint(equalTo: mainStackView!.leadingAnchor).isActive=true

        mainStackView?.addArrangedSubview(internalView2!)
        internalView2?.leadingAnchor.constraint(equalTo: internalView1!.trailingAnchor).isActive=true
        internalView2?.bottomAnchor.constraint(equalTo: internalView1!.bottomAnchor).isActive=true
        internalView2?.topAnchor.constraint(equalTo: internalView1!.topAnchor).isActive=true

        /// Add Labels
        internalView1?.addSubview(label1!)
        label1?.topAnchor.constraint(equalTo: internalView1!.topAnchor).isActive=true
        label1?.bottomAnchor.constraint(equalTo: internalView1!.bottomAnchor).isActive=true
        label1?.leadingAnchor.constraint(equalTo: internalView1!.leadingAnchor).isActive=true
        label1?.trailingAnchor.constraint(equalTo: internalView1!.trailingAnchor).isActive=true

        internalView2?.addSubview(label2!)
        label2?.topAnchor.constraint(equalTo: internalView2!.topAnchor).isActive=true
        label2?.bottomAnchor.constraint(equalTo: internalView2!.bottomAnchor).isActive=true
        label2?.leadingAnchor.constraint(equalTo: internalView2!.leadingAnchor).isActive=true
        label2?.trailingAnchor.constraint(equalTo: internalView2!.trailingAnchor).isActive=true
    }
}

Option 2 If you just want to directly add UILabels

private var totalNumberOfLabels : Int?

func addStackView(){
    mainStackView = UIStackView()
    label1 = UILabel()
    label2 = UILabel()

    label1?.text = "ios-geek"
    label2?.text = "qwerykgasgdasgdhgashdgjasgdasgdjahsdjahsgdasdjagsjdahsgdjsdhasd"
    label1?.numberOfLines = 0
    label2?.numberOfLines = 0
    label1?.textColor = UIColor.black
    label2?.textColor = UIColor.black
    label1?.backgroundColor = .red
    label2?.backgroundColor = .purple

    /// Set Translation Properties
    mainStackView?.translatesAutoresizingMaskIntoConstraints = false
    label1?.translatesAutoresizingMaskIntoConstraints = false
    label2?.translatesAutoresizingMaskIntoConstraints = false

    addContentView()
}

func addContentView(){

    /// Add Stack View Constraints
    mainStackView?.backgroundColor = UIColor.white
    self.view.addSubview(mainStackView!)
    mainStackView?.topAnchor.constraint(equalTo: self.view.topAnchor).isActive=true
    mainStackView?.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive=true
    mainStackView?.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive=true
    mainStackView?.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive=true

    /// Add First StackView
    mainStackView?.distribution = .fillEqually
    mainStackView?.axis = .horizontal
    mainStackView?.alignment = .center
    mainStackView?.spacing = 10
    mainStackView?.backgroundColor = UIColor.white

    /// Add Views
    mainStackView?.addArrangedSubview(label1!)
    label1?.topAnchor.constraint(equalTo: mainStackView!.topAnchor).isActive=true
    label1?.bottomAnchor.constraint(equalTo: mainStackView!.bottomAnchor).isActive=true
    label1?.leadingAnchor.constraint(equalTo: mainStackView!.leadingAnchor).isActive=true
    label1?.widthAnchor.constraint(equalToConstant: width/CGFloat(totalNumberOfLabels!)).isActive=true

    mainStackView?.addArrangedSubview(label2!)
    label2?.topAnchor.constraint(equalTo: label1!.topAnchor).isActive=true
    label2?.bottomAnchor.constraint(equalTo: label1!.bottomAnchor).isActive=true
    label2?.leadingAnchor.constraint(equalTo: label1!.trailingAnchor).isActive=true
    label1?.widthAnchor.constraint(equalToConstant: width/CGFloat(totalNumberOfLabels!)).isActive=true
}

Output

enter image description here

Upvotes: -1

Related Questions