我在UIView中有几个UILabel。
我将包含视图限制为100px。如果两个UILabel的内在宽度各为75px(因为它们的内容),我想要的是第二个标签落在第一个标签之下,因为如果没有包装它自己的文本就无法显示。
iOS中是否包含支持该行为的视图?
答案 0 :(得分:0)
Here is one example of "wrapping" labels based on fitting into the width of a parent view.
You can run this directly in a Playground page... Tap the red "Tap Me" button to toggle the text in the labels, to see how they "fit".
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