【问题标题】:Trying to make sense of UILabel behavior.试图理解 UILabel 的行为。
【发布时间】:2026-02-14 14:15:02
【问题描述】:

我正在尝试复制 Apple 的计算器 UI 布局。

这是我目前所拥有的gif

我遇到的问题主要与 UILables 有关。 如上图所示,我遇到了以下问题:

  • 在设备旋转时,标签“L1”和“L2”弹出,而不是平滑过渡。

  • 当转换回纵向时,棕色按钮上的标签会消失。

对于“L1”和“L2”标签,我尝试过尝试内容模式和约束,但是,我仍然会遇到笨拙的过渡。

至于消失的标签,我没有通过它的 is hidden 属性隐藏/取消隐藏堆栈视图以使布局出现和消失,而是尝试使用堆栈视图上的约束来处理转换,但是,结果仍然是一样。

我也在网上查看并尝试了一些建议,但是,大多数答案都已过时或根本不起作用。

代码非常简单,主要包括设置视图及其约束。

extension UIStackView {
    convenience init(axis: UILayoutConstraintAxis, distribution: UIStackViewDistribution = .fill) {
        self.init()
        self.axis = axis
        self.distribution = distribution
        self.translatesAutoresizingMaskIntoConstraints = false
    }
}

class Example: UIView {
    let mainStackView = UIStackView(axis: .vertical, distribution: .fill)
    let subStackView = UIStackView(axis: .horizontal, distribution: .fillProportionally)
    let portraitStackView = UIStackView(axis: .vertical, distribution: .fillEqually)
    let landscapeStackView = UIStackView(axis: .vertical, distribution: .fillEqually)

    var containerView: UIView = {
        $0.backgroundColor = .darkGray
        $0.translatesAutoresizingMaskIntoConstraints = false
        return $0
    }(UIView(frame: .zero))

    let mainView: UIView = {
        $0.backgroundColor = .blue
        $0.translatesAutoresizingMaskIntoConstraints = false
        return $0
    }(UIView(frame: .zero))

    let labelView: UIView = {
        $0.backgroundColor = .red
        $0.translatesAutoresizingMaskIntoConstraints = false
        return $0
    }(UIView(frame: .zero))

    var labelOne: UILabel!
    var labelTwo: UILabel!

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .red
        autoresizingMask = [.flexibleWidth, .flexibleHeight]

        labelOne = createLabel(text: "L1")
        labelOne.translatesAutoresizingMaskIntoConstraints = false
        labelOne.backgroundColor = .darkGray
        labelTwo = createLabel(text: "L2")
        labelTwo.translatesAutoresizingMaskIntoConstraints = false
        labelTwo.backgroundColor = .black

        landscapeStackView.isHidden = true

        mainView.addSubview(labelView)
        labelView.addSubview(labelOne)
        labelView.addSubview(labelTwo)
        addSubview(mainStackView)
        mainStackView.addArrangedSubview(mainView)

        setButtonStackView()
        setStackViewConstriants()
        setDisplayViewConstriants()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setStackViewConstriants() {
        mainStackView.translatesAutoresizingMaskIntoConstraints = false
        mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
        mainStackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
        mainStackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    }

    func setDisplayViewConstriants() {
        mainView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 288/667).isActive = true

        labelView.heightAnchor.constraint(equalTo: mainView.heightAnchor, multiplier: 128/288).isActive = true
        labelView.centerYAnchor.constraint(equalTo: mainView.centerYAnchor).isActive = true
        labelView.leadingAnchor.constraint(equalTo: mainView.leadingAnchor, constant: 24).isActive = true
        labelView.trailingAnchor.constraint(equalTo: mainView.trailingAnchor, constant: -24).isActive = true

        labelOne.heightAnchor.constraint(equalTo: labelTwo.heightAnchor, multiplier: 88/32).isActive = true
        labelOne.trailingAnchor.constraint(equalTo: labelView.trailingAnchor).isActive = true
        labelOne.leadingAnchor.constraint(equalTo: labelView.leadingAnchor).isActive = true
        labelOne.topAnchor.constraint(equalTo: labelView.topAnchor).isActive = true

        labelTwo.topAnchor.constraint(equalTo: labelOne.bottomAnchor).isActive = true
        labelTwo.trailingAnchor.constraint(equalTo: labelView.trailingAnchor).isActive = true
        labelTwo.leadingAnchor.constraint(equalTo: labelOne.leadingAnchor).isActive = true
        labelTwo.bottomAnchor.constraint(equalTo: labelView.bottomAnchor).isActive = true
    }

    func createLabel(text: String) -> UILabel {
        let label = UILabel(frame: .zero)
        label.text = text
        label.font = UIFont.init(name: "Arial-BoldMT", size: 60)
        label.textColor = .white
        label.textAlignment = .right
        label.contentMode = .right
        label.minimumScaleFactor = 0.1
        label.adjustsFontSizeToFitWidth = true
        label.numberOfLines = 0
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }

    func createButton(text: String) -> UIButton {
        let button = UIButton(type: .custom)
        button.setTitle(text, for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.layer.borderColor = UIColor.white.cgColor
        button.layer.borderWidth = 1
        button.titleLabel?.font = UIFont.init(name: "Arial-BoldMT", size: 60)
        button.titleLabel?.minimumScaleFactor = 0.1
        button.titleLabel?.adjustsFontSizeToFitWidth = true
        button.titleLabel?.translatesAutoresizingMaskIntoConstraints = false
        button.titleLabel?.leadingAnchor.constraint(equalTo: button.leadingAnchor).isActive = true
        button.titleLabel?.trailingAnchor.constraint(equalTo: button.trailingAnchor).isActive = true
        button.titleLabel?.topAnchor.constraint(equalTo: button.topAnchor).isActive = true
        button.titleLabel?.bottomAnchor.constraint(equalTo: button.bottomAnchor).isActive = true
        button.titleLabel?.textAlignment = .center
        button.titleLabel?.contentMode = .scaleAspectFill
        button.titleLabel?.numberOfLines = 0
        return button
    }

    func setButtonStackView() {
        for _ in 1...5 {
            let stackView = UIStackView(axis: .horizontal, distribution: .fillEqually)
            for _ in 1...4 {
                let button = createButton(text: "0")
                button.backgroundColor = .brown
                stackView.addArrangedSubview(button)
            }
            landscapeStackView.addArrangedSubview(stackView)
        }

        for _ in 1...5 {
            let stackView = UIStackView(axis: .horizontal, distribution: .fillEqually)
            for _ in 1...4 {
                let button = createButton(text: "0")
                button.backgroundColor = .purple
                stackView.addArrangedSubview(button)
            }
            portraitStackView.addArrangedSubview(stackView)
        }

        subStackView.addArrangedSubview(landscapeStackView)
        subStackView.addArrangedSubview(portraitStackView)
        mainStackView.addArrangedSubview(subStackView)
    }

    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        if UIDevice.current.orientation.isLandscape && landscapeStackView.isHidden == true {
            self.landscapeStackView.isHidden = false
        }
        if UIDevice.current.orientation.isPortrait && landscapeStackView.isHidden == false {
            self.landscapeStackView.isHidden = true
        }
        self.layoutIfNeeded()
    }
}

【问题讨论】:

    标签: swift autolayout uilabel autoresize uistackview


    【解决方案1】:

    概述:

    • 使用单独的组件/视图控制器以增量方式执行操作(更易于调试)
    • 以下解决方案仅适用于标签 L1 和 L2。
    • 对于计算器按钮,最好使用UICollectionViewController。 (我还没有实现,添加为子视图控制器)

    代码:

    private func setupLabels() {
        
        view.backgroundColor = .red
        
        let stackView           = UIStackView()
        stackView.axis          = .vertical
        stackView.alignment     = .fill
        stackView.distribution  = .fill
        
        stackView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(stackView)
        
        stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
        
        
        let label1 = UILabel()
        label1.text             = "L1"
        label1.textColor        = .white
        label1.backgroundColor  = .darkGray
        label1.textAlignment    = .right
        label1.font = UIFont.preferredFont(forTextStyle: .title1)
        
        
        let label2 = UILabel()
        label2.text             = "L2"
        label2.textColor        = .white
        label2.backgroundColor  = .black
        label2.textAlignment    = .right
        label2.font = UIFont.preferredFont(forTextStyle: .caption1)
        
        stackView.addArrangedSubview(label1)
        stackView.addArrangedSubview(label2)
    }
    

    【讨论】:

    • 正如我之前所说,我主要关心的是 UILabel。我已经尝试在堆栈视图中添加标签,但是结果保持不变。该项目主要用于学习目的,这就是我使用堆栈视图的原因