Vertically align text within a UILabel (Note : Using AutoLayout)

前端 未结 17 2655
忘掉有多难
忘掉有多难 2020-12-23 02:24

I am Copying the same Question asked Before Question. I have tried the solutions given and was not able to solve it since sizetofit was not effective when I

相关标签:
17条回答
  • 2020-12-23 02:43

    Edit

    In my original answer I was using the paragraph style of the label. Turns out that for multi-line labels this actually prevents the label from being multi-line. As a result I removed it from the calculation. See more about this in Github

    For those of you more comfortable with using Open Source definitely look at TTTAttributedLabel where you can set the label's text alignment to TTTAttributedLabelVerticalAlignmentTop


    The trick is to subclass UILabel and override drawTextInRect. Then enforce that the text is drawn at the origin of the label's bounds.

    Here's a naive implementation that you can use right now:

    Swift

    @IBDesignable class TopAlignedLabel: UILabel {
        override func drawTextInRect(rect: CGRect) {
            if let stringText = text {
                let stringTextAsNSString = stringText as NSString
                var labelStringSize = stringTextAsNSString.boundingRectWithSize(CGSizeMake(CGRectGetWidth(self.frame), CGFloat.max),
                    options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                    attributes: [NSFontAttributeName: font],
                    context: nil).size
                super.drawTextInRect(CGRectMake(0, 0, CGRectGetWidth(self.frame), ceil(labelStringSize.height)))
            } else {
                super.drawTextInRect(rect)
            }
        }
        override func prepareForInterfaceBuilder() {
            super.prepareForInterfaceBuilder()
            layer.borderWidth = 1
            layer.borderColor = UIColor.blackColor().CGColor
        }
    }
    

    Swift 3

      @IBDesignable class TopAlignedLabel: UILabel {
        override func drawText(in rect: CGRect) {
            if let stringText = text {
                let stringTextAsNSString = stringText as NSString
                let labelStringSize = stringTextAsNSString.boundingRect(with: CGSize(width: self.frame.width,height: CGFloat.greatestFiniteMagnitude),
                                                                                options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                                                                attributes: [NSFontAttributeName: font],
                                                                                context: nil).size
                super.drawText(in: CGRect(x:0,y: 0,width: self.frame.width, height:ceil(labelStringSize.height)))
            } else {
                super.drawText(in: rect)
            }
        }
        override func prepareForInterfaceBuilder() {
            super.prepareForInterfaceBuilder()
            layer.borderWidth = 1
            layer.borderColor = UIColor.black.cgColor
        }
    }
    

    Objective-C

    IB_DESIGNABLE
    @interface TopAlignedLabel : UILabel
    
    @end
    
    @implementation TopAlignedLabel
    
    - (void)drawTextInRect:(CGRect)rect {
        if (self.text) {
            CGSize labelStringSize = [self.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(self.frame), CGFLOAT_MAX)
                                                             options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                                          attributes:@{NSFontAttributeName:self.font}
                                                             context:nil].size;
            [super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)),ceilf(labelStringSize.height))];
        } else {
            [super drawTextInRect:rect];
        }
    }
    
    - (void)prepareForInterfaceBuilder {
            [super prepareForInterfaceBuilder];
            self.layer.borderWidth = 1;
            self.layer.borderColor = [UIColor blackColor].CGColor;
    }
    
    @end
    

    Since I used IBDesignable you can add this label to a storyboard and watch it go, this is what it looks like for me

    enter image description here

    0 讨论(0)
  • Swift 4

    You should subclass UILabel and override text display rendering.

    class UITopAlignedLabel: UILabel {
        override func drawText(in rect: CGRect) {
            guard let string = text else {
                super.drawText(in: rect)
                return
            }
    
            let size = (string as NSString).boundingRect(
                with: CGSize(width: rect.width, height: .greatestFiniteMagnitude),
                options: [.usesLineFragmentOrigin],
                attributes: [.font: font],
                context: nil).size
    
            var rect = rect
            rect.size.height = size.height.rounded()
            super.drawText(in: rect)
        }
    }
    
    0 讨论(0)
  • 2020-12-23 02:46

    For me, I didn't set the height constraint, the text always grows from the top of the label. The constraints for this label are top, left, right. By the way, my label has fixed line numbers, so no worries about the height.

    0 讨论(0)
  • 2020-12-23 02:47
      @IBInspectable var alignTop: Bool = false
    
      func setAlignTop() {
        let text = self.text!
        let lines = text.characters.split(separator: "\n").count
        if lines < self.numberOfLines {
          var newLines = ""
          for _ in 0..<(self.numberOfLines - lines) {
            newLines = newLines.appending("\n ")
          }
          self.text! = text.appending(newLines)
        }
      }
    
      override var text: String? {
        didSet {
          if alignTop {
            self.setAlignTop()
          }
        }
      }
    
    0 讨论(0)
  • 2020-12-23 02:49

    Here's an improvement on the Swift 3 solution by Daniel Galasko (here you can also set the maximum line number without an offset on the top):

    import UIKit
    
    @IBDesignable class TopAlignedLabel: UILabel {
        override func drawText(in rect: CGRect) {
            if let stringText = text {
                let stringTextAsNSString = stringText as NSString
                let labelString = stringTextAsNSString.boundingRect(with: CGSize(width: frame.width, height: .greatestFiniteMagnitude),
                        options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
                super.drawText(in: CGRect(x: 0, y: 0, width: frame.width, height: ceil(labelString.size.height) > frame.height ? frame.height : ceil(labelString.size.height)))
            } else {
                super.drawText(in: rect)
            }
        }
    
        override func prepareForInterfaceBuilder() {
            super.prepareForInterfaceBuilder()
            layer.borderWidth = 1
            layer.borderColor = UIColor.black.cgColor
        }
    }
    
    0 讨论(0)
  • 2020-12-23 02:51

    Instead, I changed the Bottom Space Constant to priority @250 and solved my problem. And my label has height constant with <= constant

    0 讨论(0)
提交回复
热议问题