Программно созданный горизонтальный UIStackView с двумя выровненными метками

Я хочу 2 метки (скажем, leftLabel, rightLabel) и расположить их горизонтально так, чтобы левая метка растягивалась, а правая метка просто соответствовала значку с одним символом (скажем, «>»). Таким образом, расположение обеих этикеток оправдано. Как это...

введите здесь описание изображения

Это код, который у меня есть -

class StackViewController: UIViewController {
    /// Main vertical outer/container stack view that pins its edges to this view in storyboard (i.e. full screen)
    @IBOutlet weak private var containerStackView: UIStackView!

    private var leftLabel: UILabel = {
        let leftLabel = UILabel(frame: .zero)
        leftLabel.font = .preferredFont(forTextStyle: .body)
        leftLabel.numberOfLines = 0 // no text truncation, allows wrap
        leftLabel.backgroundColor = .orange
        return leftLabel
    }()
    private var rightLabel: UILabel = {
        let rightLabel = UILabel(frame: .zero)
        rightLabel.font = .preferredFont(forTextStyle: .body)
        // Set CHCR as high so that label sizes itself to fit the text
        rightLabel.setContentHuggingPriority(UILayoutPriorityDefaultHigh, for: .horizontal)
        rightLabel.setContentCompressionResistancePriority(UILayoutPriorityDefaultHigh, for: .horizontal)
        rightLabel.backgroundColor = .green
        return rightLabel
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        prepareAndLoadSubViews()
        // Note, the text required to be set in viewDidAppear, not viewDidLoad, otherwise rightLabel stretches to fill!!
        leftLabel.text = "This is left label text that may go in multiple lines"
        rightLabel.text = ">"   // Always a single character
    }

    /// Dynamically creates a horizontal stack view, with 2 labels, in the container stack view
    private func prepareAndLoadSubViews() {
        /// Prepare the horizontal label stack view and add the 2 labels
        let labelStackView = UIStackView(arrangedSubviews: [leftLabel, rightLabel])
        labelStackView.axis = .horizontal
        labelStackView.distribution = .fillProportionally
        labelStackView.alignment = .top
        containerStackView.addArrangedSubview(labelStackView)
        containerStackView.addArrangedSubview(UIView())
    }
}

Что дает результат ниже (т.е. ширина leftLabel равна 0 в отладчике представления) -

введите здесь описание изображения

ПРИМЕЧАНИЕ. Если я перемещаю код набора текста в viewDidAppear, он работает нормально.

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // Note, the text required to be set in viewDidAppear, not viewDidLoad, otherwise rightLabel stretches to fill!!
    leftLabel.text = "This is left label text that may go in multiple lines"
    rightLabel.text = ">"   // Always a single character
}

Почему? И можем ли мы установить приоритеты защиты контента/сопротивления сжатию перед viewDidLoad?


person Ashok    schedule 09.11.2017    source источник
comment
Извините, что я, возможно, немного опоздал на вечеринку, но вы не пробовали немного повысить приоритет rightLabel.setContentHuggingPriority(.defaultHigh + 25, for: .horizontal)?   -  person Stimorol    schedule 24.10.2019


Ответы (1)


Я немного поигрался с вашим кодом, но тоже не смог заставить его работать. Я думаю, что это ошибка, которая возникает, когда вы добавляете UIStackView к другому UIStackView. Когда у вас есть только один UIStackView, ваш код работает нормально.

Поэтому я не могу предложить исправление для вашего случая, но ИМХО вам вообще не нужно использовать UIStackView для ваших двух ярлыков. UIStackView отлично подходит, если у вас есть несколько упорядоченных подвидов, которые вы скрываете и показываете, и их нужно упорядочивать автоматически. Для двух «статических» меток я думаю, что это перебор.

Вы можете добиться того, что вам нужно, добавив две метки в UIView, а затем установив ограничения макета для меток. Это очень просто:

class StackViewController: UIViewController {

    @IBOutlet weak var containerStackView: UIStackView!

    private var leftLabel: UILabel = {
        let leftLabel = UILabel(frame: .zero)
        leftLabel.font = .preferredFont(forTextStyle: .body)
        leftLabel.numberOfLines = 0
        leftLabel.backgroundColor = .orange
        leftLabel.translatesAutoresizingMaskIntoConstraints = false
        leftLabel.numberOfLines = 0
        return leftLabel
    }()
    private var rightLabel: UILabel = {
        let rightLabel = UILabel(frame: .zero)
        rightLabel.font = .preferredFont(forTextStyle: .body)
        rightLabel.setContentHuggingPriority(UILayoutPriority.required, for: .horizontal)
        rightLabel.backgroundColor = .green
        rightLabel.translatesAutoresizingMaskIntoConstraints = false
        return rightLabel
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        prepareAndLoadSubViews()
        leftLabel.text = "This is left label text that may go in multiple lines"
        rightLabel.text = ">"
    }

    private func prepareAndLoadSubViews() {
        let labelContainerView = UIView()
        labelContainerView.addSubview(leftLabel)
        labelContainerView.addSubview(rightLabel)

        NSLayoutConstraint.activate([
            leftLabel.leadingAnchor.constraint(equalTo: labelContainerView.leadingAnchor),
            leftLabel.topAnchor.constraint(equalTo: labelContainerView.topAnchor),
            leftLabel.bottomAnchor.constraint(equalTo: labelContainerView.bottomAnchor),

            rightLabel.leadingAnchor.constraint(equalTo: leftLabel.trailingAnchor),
            rightLabel.topAnchor.constraint(equalTo: labelContainerView.topAnchor),
            rightLabel.bottomAnchor.constraint(equalTo: labelContainerView.bottomAnchor),
            rightLabel.trailingAnchor.constraint(equalTo: labelContainerView.trailingAnchor)
        ])

        containerStackView.addArrangedSubview(labelContainerView)
        containerStackView.addArrangedSubview(UIView())
    }
}
person joern    schedule 09.11.2017
comment
Большое спасибо за попытку и советы. Вы правы, я могу добиться того же макета, используя ограничения. Но у меня есть дополнительные требования, чтобы скрыть rightLabel на основе данных, т.е. leftLabel должен растягиваться на всю ширину. Достижение этого с помощью автоматической компоновки потребует еще нескольких дополнительных ограничений. Кроме того, в целом в этой ячейке мне вскоре придется добавить еще несколько меток/изображений, поэтому я предпочел бы использовать представление стека, а не ограничения. - person Ashok; 09.11.2017