繁体   English   中英

将iOS UILabel包装为受约束的UIView中的一个块

[英]Wrapping an iOS UILabel as a block within a constrained UIView

我在UIView中有几个UILabel。

我将包含视图的像素限制为100px。 如果两个UILabel的固有宽度均为75px(由于它们的内容),我想要的是第二个标签低于第一个标签,因为如果不包装它自己的文本就无法显示它。

iOS中是否包含一个支持该行为的View?

这是一个基于适合父视图宽度的“包装”标签的示例。

您可以直接在Playground页面中运行此程序...点击红色的“ Tap Me”按钮以切换标签中的文本,以查看它们如何“适合”。

import UIKit
import PlaygroundSupport

// String extension for easy text width/height calculations
extension String {
    func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        return ceil(boundingBox.height)
    }

    func width(withConstrainedHeight height: CGFloat, font: UIFont) -> CGFloat {
        let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
        let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [.font: font], context: nil)
        return ceil(boundingBox.width)
    }
}

class TestViewController : UIViewController {

    let btn: UIButton = {
        let b = UIButton()
        b.translatesAutoresizingMaskIntoConstraints = false
        b.setTitle("Tap Me", for: .normal)
        b.backgroundColor = .red
        return b
    }()

    let cView: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .blue
        return v
    }()

    let labelA: UILabel = {
        let v = UILabel()
        // we will be explicitly setting the label's frame
        v.translatesAutoresizingMaskIntoConstraints = true
        v.backgroundColor = .yellow
        return v
    }()

    let labelB: UILabel = {
        let v = UILabel()
        // we will be explicitly setting the label's frame
        v.translatesAutoresizingMaskIntoConstraints = true
        v.backgroundColor = .cyan
        return v
    }()

    // spacing between labels when both fit on one line
    let spacing: CGFloat = 8.0

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(btn)

        btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)

        btn.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        btn.topAnchor.constraint(equalTo: view.topAnchor, constant: 20.0).isActive = true

        // add the "containing" view
        view.addSubview(cView)

        // add the two labels to the containing view
        cView.addSubview(labelA)
        cView.addSubview(labelB)

        // constrain containing view Left-20, Top-20 (below the button)
        cView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0).isActive = true
        cView.topAnchor.constraint(equalTo: btn.bottomAnchor, constant: 20.0).isActive = true

        // containing view has a fixed width of 100
        cView.widthAnchor.constraint(equalToConstant: 100.0).isActive = true

        // constrain bottom of containing view to bottom of labelB (so the height auto-sizes)
        cView.bottomAnchor.constraint(equalTo: labelB.bottomAnchor, constant: 0.0).isActive = true

        // initial text in the labels - both will fit "on one line"
        labelA.text = "First"
        labelB.text = "Short"

    }

    func updateLabels() -> Void {
        // get the label height based on its font
        if let h = labelA.text?.height(withConstrainedWidth: CGFloat.greatestFiniteMagnitude, font: labelA.font) {
            // get the calculated width of each label
            if let wA = labelA.text?.width(withConstrainedHeight: h, font: labelA.font),
                let wB = labelB.text?.width(withConstrainedHeight: h, font: labelB.font) {

                // labelA frame will always start at 0,0
                labelA.frame = CGRect(x: 0.0, y: 0.0, width: wA, height: h)

                // will both labels + spacing fit in the containing view's width?
                if wA + wB + spacing <= cView.frame.size.width {
                    // yes, so place labelB to the right of labelA (put them on "one line")
                    labelB.frame = CGRect(x: wA + spacing, y: 0.0, width: wB, height: h)
                } else {
                    // no, so place labelB below labelA ("wrap" labelB "to the next line")
                    labelB.frame = CGRect(x: 0.0, y: h, width: wB, height: h)
                }
            }
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        updateLabels()
    }

    @objc func didTap(_ sender: Any?) -> Void {
        // toggle labelB's text
        if labelB.text == "Short" {
            labelB.text = "Longer text"
        } else {
            labelB.text = "Short"
        }
        // adjust size / position of labels
        updateLabels()
    }

}


let vc = TestViewController()
vc.view.backgroundColor = .white
PlaygroundPage.current.liveView = vc

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM