Ben Ong
Ben Ong

Reputation: 931

Aligning multiple runtime generated UILabels in a UITableView

I have a UITableView that needs to support content by listing style something like

enter image description here

But the tricky part is that the amount of "Label" will vary with each cell, some may have only 4 but also up to 12. Also the "Value" can be either a single word or short phrase up to two lines(like in the image above). So I decided to use UIStackView to help me pack and size my UILabels used to display this. I am currently stuck at a problem when the "Label" varies in length like: enter image description here

I need the leading end of the "Values" to be aligned like the first image even though the "Label" vary in length. Is there a behaviour of UIStackView that allows so? Or is there another approach that can allow me to obtain the results I need?

Note: Each "Label" and "Value" is in one UIStackView, I did it to align them.

I tried using String Formatting too, but "Values" with more than one line will wrap under the label instead of wrapping by itself like I manage to do in the images.

I tried placing all "Labels" in one UIStackView and all "Values" in another, I could no get them to align like they do in the images once the "Value" is more than one line.

Or if it might be a mistake I made somewhere, this is how I created the UIStackViews:

    var higherCount = 0
    if labels.count<values.count {
        higherCount = values.count
    } else {
        higherCount = labels.count
    }

    mainStackView = UIStackView(frame: CGRect(x: 0, y: 0, width: frame.width, height: frame.height))

    for i in 0..<higherCount {
        var height:CGFloat = 20
        if (values[i] as NSString).size(attributes: [NSFontAttributeName:UIFont.systemFont(ofSize: UIFont.systemFontSize)]).width > frame.width {
            height = 42
        }

        let stackViewToAdd = UIStackView(frame: CGRect(x: 0, y: 0, width: frame.width, height: height))

        let label = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: height))
        if labels.count<higherCount {
            label.text = ""
        } else {
            label.text = labels[i]
        }
        label.setContentCompressionResistancePriority(1000, for: .horizontal)
        label.setContentHuggingPriority(999, for: .horizontal)
        stackViewToAdd.addArrangedSubview(label)
        let value = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: height))
        if values.count<higherCount {
            value.text = ""
        } else {
            value.text = values[i]
        }
        value.lineBreakMode = .byWordWrapping
        value.numberOfLines = 2
        stackViewToAdd.addArrangedSubview(value)
        mainStackView?.addArrangedSubview(stackViewToAdd)
    }
    mainStackView?.alignment = .fill
    mainStackView?.axis = .vertical
    mainStackView?.distribution = .fill

Upvotes: 0

Views: 210

Answers (1)

Vinupriya Arivazhagan
Vinupriya Arivazhagan

Reputation: 564

I you created your cell programmatically, then you can resize the cell programmatically depends on the size of UILabel Content.

In my case Label font is UIFont.systemFont(ofSize: 15), minimum TableViewCell height is 50, arrLabel1 and arrLabel2 will be the content of Labels.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return arrLable1.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: "\(indexPath.row)")

    let lable1 = UILabel(frame: CGRect(x: 10, y: 10, width: view.frame.size.width - 20, height: 0))
    lable1.numberOfLines = 0
    lable1.font = UIFont.systemFont(ofSize: 15)
    lable1.text = arrLable1[indexPath.row]
    lable1.sizeToFit()
    cell.addSubview(lable1)

    let lable2 = UILabel(frame: CGRect(x: 10, y: lable1.frame.origin.y + lable1.frame.size.height + 10 , width: view.frame.size.width - 20, height: 0))
    lable2.numberOfLines = 0
    lable2.font = UIFont.systemFont(ofSize: 15)
    lable2.text = arrLable2[indexPath.row]
    lable2.sizeToFit()
    cell.addSubview(lable2)

    return cell
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)
    -> CGFloat {

    let boundingRect1 = arrLable1[indexPath.row].boundingRect(with: CGSize(width: view.frame.size.width - 40 , height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin,attributes:[ NSFontAttributeName : UIFont.systemFont(ofSize: 15)] ,context: nil)

    let boundingRect2 = arrLable2[indexPath.row].boundingRect(with: CGSize(width: view.frame.size.width - 40 , height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin,attributes:[ NSFontAttributeName : UIFont.systemFont(ofSize: 15)] ,context: nil)

    guard boundingRect1.height + boundingRect2.height + 30 > 50 else {
        return 50
    }

    return boundingRect1.height + boundingRect2.height + 30
}

Set Label numberOfLines to 0

Upvotes: 1

Related Questions