Paul Hadfield
Paul Hadfield

Reputation: 6136

Setting label's text causes stack view layout issues

If I use the code sample below in a playground everything looks good until I try to set the text property for one of the labels. I've pinned it down to being able to change the value before adding the stack view to the UIView. But if I change the label's text value after adding the stack view to the parent view then the two labels end up overlapping (see images at bottom).

This is a basic test harness highlighting the problem, the real code will set the value at run time, potentially after the view has loaded, and the actual control is more complex than this. I know it's something to do with auto-layout / constraints but I'm pretty sure I've followed the example code I was looking at properly but I can't see the difference between their example and mine.

import UIKit
import PlaygroundSupport

@IBDesignable
public final class TestHarness : UIView {

    fileprivate let nameLabel: UILabel = {
        let label = UILabel()
        //label.font = UIFont.systemFont( ofSize: 20, weight: UIFont.Weight.medium)
        label.textAlignment = .center
        label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
        label.text = "Person's Name"
        label.adjustsFontSizeToFitWidth = true
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()

    fileprivate let jobTitleLabel: UILabel = {
        let label = UILabel()
        //label.font = UIFont.systemFont( ofSize: 20, weight: UIFont.Weight.medium)
        label.textAlignment = .center
        label.textColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1)
        label.text = "Job Title"
        label.adjustsFontSizeToFitWidth = true
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()


    fileprivate lazy var stackView: UIStackView = {
        let stackView = UIStackView()
        stackView.axis = .vertical
        //stackView.distribution = .fill
        stackView.alignment = .center
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.addArrangedSubview(self.nameLabel)
        stackView.addArrangedSubview(self.jobTitleLabel)
        return stackView
    }()

    public required init?(coder: NSCoder) {
        super.init(coder: coder)
        initPhase2()
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        initPhase2()
    }

    private func initPhase2() {

        layer.cornerRadius = 10
        layer.borderWidth = 2
        self.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)

        jobTitleLabel.backgroundColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
        // self.jobTitleLabel.text = "Developer" // <<-- Can set here no problem

        self.addSubview(stackView)
        // self.jobTitleLabel.text = "Developer" // << -- If I set this here, job title and name overlap

        NSLayoutConstraint.activate([
            {
                let constraint = stackView.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor, constant: 8)
                constraint.priority = UILayoutPriority(750)
                return constraint
            }(),
            stackView.leftAnchor.constraint(equalTo: layoutMarginsGuide.leftAnchor, constant: 8),
            stackView.rightAnchor.constraint(equalTo: layoutMarginsGuide.rightAnchor, constant: -8),
            {
                let constraint = stackView.bottomAnchor.constraint(lessThanOrEqualTo: layoutMarginsGuide.bottomAnchor, constant: -8)
                constraint.priority = UILayoutPriority(750)
                return constraint
            }(),
            stackView.centerYAnchor.constraint(equalTo: layoutMarginsGuide.centerYAnchor)
        ])
    }

    @IBInspectable
    public var Name: String? {
        get{
            return self.nameLabel.text
        }
        set{
            self.nameLabel.text = newValue
        }
    }

    @IBInspectable
    public var JobTitle: String? {
        get{
            return self.jobTitleLabel.text
        }
        set{
            self.jobTitleLabel.text = newValue
        }
    }
}

let dimensions = (width: 200, height: 300)
let control = TestHarness(frame: CGRect(x: dimensions.width / 2, y: dimensions.height / 2, width: dimensions.width, height: dimensions.height))
// control.JobTitle = "Developer" // << -- If I set this here, job title and name overlap

let view = UIView(frame: control.frame.insetBy(dx: -100, dy: -100))
view.backgroundColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
view.addSubview(control)

PlaygroundPage.current.liveView = view

This is how it should look, and does look if I change the label's text before adding the stack view as a child of the parent view.

enter image description here

This is how it looks if I change the label's text after adding the stack view to the parent view. The job title overlaps with the name label.

enter image description here

Upvotes: 0

Views: 1238

Answers (1)

mxlbx
mxlbx

Reputation: 26

Your code looks good, maybe there is a problem with Playground's render loop, I created an Xcode project and used your code and it worked great in the simulator with no overlap:

    import UIKit

override func viewDidLoad() {
    super.viewDidLoad()
    let dimensions = (width: 200, height: 300)
    let control = TestHarness(frame: CGRect(x: dimensions.width / 2, y: dimensions.height / 2, width: dimensions.width, height: dimensions.height))

    control.JobTitle = "Developer b2.0" // << -- No overlap in simulator

    let contentView = UIView(frame: control.frame.insetBy(dx: -100, dy: -100))
    contentView.backgroundColor = #colorLiteral(red: 0.3098039329, green: 0.01568627544, blue: 0.1294117719, alpha: 1)
    contentView.addSubview(control)
    view.addSubview(contentView)
}

Upvotes: 1

Related Questions