简体   繁体   English

具有UIView子类化的自定义UILabel

[英]Self-sizing UILabel with UIView Subclassing

I have a sketch and I'm trying to achieve the concept programmatically. 我有一个草图,我正在尝试以编程方式实现这一概念。

Here's my Sketch: 这是我的草图:

在此处输入图片说明

Achievement: 成就: 在此处输入图片说明

My code: 我的代码:

let luna = UIView()

let descStrWidth = messageDescription.stringWidth  // Using String Extension i made.
let descStrHeight = messageDescription.stringHeight // Using String Extension i made.

let titleStrHeight = messageTitle.stringHeight // ..
let titleStrWidth = messageTitle.stringWidth // .. 


let titleLabel = UILabel()
titleLabel.text = messageTitle
titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
titleLabel.textColor = UIColor(hexString: "4A4A4A")
titleLabel.numberOfLines = 0 // whole idea of self-sizing 

let titleDesc = UILabel()
titleDesc.text = messageDescription
titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
titleDesc.textColor = UIColor(hexString: "4A4A4A")
titleDesc.numberOfLines = 0

// My mainView 

luna.frame = CGRect(x: 16, y: 40, width: screenWidth - 30, height: titleStrHeight + descStrHeight)
luna.center.x = luna.center.x
luna.backgroundColor = .white
luna.addShadow(radius: 11, opacity: 0.2) // Some Shadow
luna.layer.cornerRadius = 10

titleDesc.frame = CGRect(x: luna.frame.minX + 3, y: titleLabel.frame.maxY + titleStrHeight, width: luna.frame.width, height: descStrHeight)


titleLabel.frame = CGRect(x: luna.frame.minX + 3, y: 8, width: titleStrWidth, height: titleStrHeight)

luna.addSubview(titleLabel)
luna.addSubview(titleDesc)
self.view.addSubview(luna)

Getting provided string width and height: 获取提供的字符串宽度和高度:

 extension String {
    var stringWidth: CGFloat {
        let constraintRect = CGSize(width: UIScreen.main.bounds.width, height: .greatestFiniteMagnitude)
        let boundingBox = self.trimmingCharacters(in: .whitespacesAndNewlines).boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)], context: nil)
        return boundingBox.width
    }

    var stringHeight: CGFloat {
        let constraintRect = CGSize(width: UIScreen.main.bounds.width, height: .greatestFiniteMagnitude)
        let boundingBox = self.trimmingCharacters(in: .whitespacesAndNewlines).boundingRect(with: constraintRect, options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 14)], context: nil)
        return boundingBox.height
    }
}

This is what I achieved so far. 这是我到目前为止所取得的成就。 This might be easier with constraints, right? 受约束可能会更容易,对吧?

救命

Here's a solution by using auto layout and stack views. 这是使用自动布局和堆栈视图的解决方案。

let luna = UIView()

let titleLabel = UILabel()
titleLabel.text = "Title"
titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
//titleLabel.textColor = UIColor(hexString: "4A4A4A")
titleLabel.numberOfLines = 0 // whole idea of self-sizing

let titleDesc = UILabel()
titleDesc.text = "Description"
titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
//titleDesc.textColor = UIColor(hexString: "4A4A4A")
titleDesc.numberOfLines = 0

// My mainView

luna.backgroundColor = .white
//luna.addShadow(radius: 11, opacity: 0.2) // Some Shadow
luna.layer.cornerRadius = 10

let verticalStackView = UIStackView(arrangedSubviews: [titleLabel, titleDesc])
verticalStackView.axis = .vertical

let okButton = UIButton()
okButton.setTitleColor(.blue, for: .normal)
okButton.setTitle("Okay", for: .normal)
okButton.setContentHuggingPriority(.defaultHigh, for: .horizontal) // to stretch the okay button horizontally

let horizontalStackView = UIStackView(arrangedSubviews: [verticalStackView, okButton])
horizontalStackView.axis = .horizontal

luna.addSubview(horizontalStackView)
view.addSubview(luna)

horizontalStackView.translatesAutoresizingMaskIntoConstraints = false // when using autolayout from code, this property must be false, otherwise constraint won't work
luna.translatesAutoresizingMaskIntoConstraints = false

// This method activates all constraint (when you create a constraint with anchors, by default they are disabled)
NSLayoutConstraint.activate([
        horizontalStackView.topAnchor.constraint(equalTo: luna.topAnchor, constant: 8),
        horizontalStackView.bottomAnchor.constraint(equalTo: luna.bottomAnchor, constant: -8),
        horizontalStackView.leadingAnchor.constraint(equalTo: luna.leadingAnchor, constant: 8),
        horizontalStackView.trailingAnchor.constraint(equalTo: luna.trailingAnchor, constant: -8),

        luna.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30),
        luna.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30),
        luna.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30)
    ])

You can change distances between views by changing spacing property of stack view. 您可以通过更改堆栈视图的spacing属性来更改视图之间的距离。 (ie horizontalStackView.spacing = 16 to put a space of 16 points between the two label and the okay button) (即horizontalStackView.spacing = 16以便在两个标签和确定按钮之间放置16个点的空间)

You can try this 你可以试试这个

 override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.


    let luna = UIView()

    let titleLabel = UILabel()
    titleLabel.text = "messageTitle"
    titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
    titleLabel.textColor = UIColor.blue
    titleLabel.numberOfLines = 0 // whole idea of self-sizing

    let titleDesc = UILabel()
    titleDesc.text = "messageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionmessageDescriptionvmessageDescriptionmessageDescriptionmessageDescription"
    titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
    titleDesc.textColor = UIColor.red
    titleDesc.numberOfLines = 0

    let btn = UIButton()
    btn.setTitle("Okay", for: .normal)
    btn.setTitleColor(UIColor.blue, for: .normal)

    luna.translatesAutoresizingMaskIntoConstraints = false
    titleLabel.translatesAutoresizingMaskIntoConstraints = false
    titleDesc.translatesAutoresizingMaskIntoConstraints = false
    btn.translatesAutoresizingMaskIntoConstraints = false

    self.view.addSubview(luna)

    luna.layer.cornerRadius = 10
    luna.addSubview(titleLabel)
    luna.addSubview(titleDesc)
    luna.addSubview(btn)
    luna.layer.borderColor = UIColor.green.cgColor
    luna.layer.borderWidth = 2
    btn.setContentCompressionResistancePriority(UILayoutPriority(rawValue: 1000), for: .horizontal)

    NSLayoutConstraint.activate([

          luna.topAnchor.constraint(equalTo: view.topAnchor, constant: 30),
          luna.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
          luna.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),

          titleLabel.topAnchor.constraint(equalTo: luna.topAnchor, constant: 20),
          titleLabel.leadingAnchor.constraint(equalTo: luna.leadingAnchor, constant: 20),
          titleLabel.trailingAnchor.constraint(equalTo: btn.leadingAnchor, constant: -10),


          titleDesc.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 10),
          titleDesc.leadingAnchor.constraint(equalTo: luna.leadingAnchor, constant: 20),
          titleDesc.trailingAnchor.constraint(equalTo: btn.leadingAnchor, constant: -10),
          titleDesc.bottomAnchor.constraint(equalTo: luna.bottomAnchor, constant: -20),


          btn.centerYAnchor.constraint(equalTo: luna.centerYAnchor, constant: 0 ),
          btn.trailingAnchor.constraint(equalTo: luna.trailingAnchor, constant: -20),


        ])

  }

在此处输入图片说明

As people are saying I would suggest you look into auto layout for the future but for now this should do what you want: 正如人们所说的,我建议您将来考虑自动布局,但是现在这应该可以满足您的要求:

    let luna = UIView()

    let titleLabel = UILabel()
    titleLabel.text = messageTitle
    titleLabel.font = UIFont(name: "avenirnext-demibold", size: 13)
    titleLabel.textColor = UIColor(hexString: "4A4A4A")
    titleLabel.numberOfLines = 0 // whole idea of self-sizing

    let titleDesc = UILabel()
    titleDesc.text = messageDescription
    titleDesc.font = UIFont(name: "avenirnext-regular", size: 9)
    titleDesc.textColor = UIColor(hexString: "4A4A4A")
    titleDesc.numberOfLines = 0

    // My mainView

    // The margins of the labels inside the luna view.
    let margins = UIEdgeInsetsMake(8, 4, 8, 4)

    // The vertical space between the two labels.
    let labelSpace: CGFloat = 4

    let titleDescSize = titleDesc.sizeThatFits(CGSize(width: screenWidth - 30 - margins.left - margins.right, height: .greatestFiniteMagnitude))

    let titleLabelSize = titleLabel.sizeThatFits(CGSize(width: screenWidth - 30 - margins.left - margins.right, height: .greatestFiniteMagnitude))

    luna.frame = CGRect(x: 16, y: 40, width: screenWidth - 30, height: titleLabelSize.height + titleDescSize.height + margins.top + margins.bottom + labelSpace)
    luna.center.x = luna.center.x
    luna.backgroundColor = .white
    luna.layer.borderWidth = luna.addShadow(radius: 11, opacity: 0.2) // Some Shadow
    luna.layer.cornerRadius = 10

    titleLabel.frame = CGRect(x: margins.left, y: margins.top, width: titleLabelSize.width, height: titleLabelSize.height)

    titleDesc.frame = CGRect(x: margins.left, y: titleLabel.frame.origin.y + titleLabel.frame.size.height + labelSpace, width: titleDescSize.width, height: titleDescSize.height)

    luna.addSubview(titleLabel)
    luna.addSubview(titleDesc)
    self.view.addSubview(luna)

Note: You don't even need to use you string extension as the UILabels are capable of calculating the size they need based on their current settings. 注意:您甚至不需要使用字符串扩展名,因为UILabels能够根据其当前设置来计算所需的大小。

You are using avenirnext-demibold and avenirnext-regular custom font. 您正在使用avenirnext-demiboldavenirnext-regular自定义字体。 But when you are calculating string width and Height you're using system font UIFont.systemFont(ofSize: 14) . 但是,当您计算字符串的宽度和高度时,您正在使用系统字体UIFont.systemFont(ofSize:14) You're not getting the correct width and height of the string. 您没有得到正确的字符串宽度和高度。 titleLabel and titleDesc both labels width should be less then luna width for Padding . titleLabel和titleDesc两个标签的宽度应小于Padding的 luna宽度。

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

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