ios - Alinhar verticalmente o texto dentro de um UILabel(Nota: Usando o AutoLayout)



objective-c cocoa-touch (11)

Swift 4

Você deve subclassificar UILabel e substituir a renderização de exibição de texto.

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)
    }
}

https://src-bin.com

Estou copiando a mesma pergunta feita antes da Question . Tentei as soluções fornecidas e não consegui resolvê-lo, pois o sizetofit não era eficaz quando eu uso o Autolayout.

A exibição esperada é como abaixo.


Answer #1

Aqui está uma melhoria na solução Swift 3 de Daniel Galasko (aqui você também pode definir o número máximo de linhas sem um deslocamento na parte superior):

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
    }
}

Answer #2

Eu tive o mesmo problema, e foi assim que resolvi. Acabei de editar a linha de base em Inspetor de atributos para o rótulo. Defina-o como "Alinhar centros".


Answer #3

Eu tive um problema semelhante em que havia três rótulos. O rótulo do meio pode ter texto muito mais longo que os outros dois, portanto, sua altura pode aumentar muito.

Como isso:

Defino a restrição de espaço inferior do rótulo do meio como> = o rótulo inferior.

Isso resolveu meu problema.


Answer #4

O layout automático funciona apenas com bordas / tamanhos do controlador, não com o conteúdo dos controladores. Portanto, não é uma boa idéia usar o layout automático para exibir o texto da etiqueta na parte superior da primeira linha. de acordo comigo, sizetofit é a melhor opção para fazer isso.


Answer #5

Para mim, não defini a restrição de altura, o texto sempre cresce da parte superior do rótulo. As restrições para esse rótulo são superior, esquerda, direita. A propósito, minha etiqueta tem números de linha fixos, portanto, não se preocupe com a altura.


Answer #6

Usei a solução de @Daniel Golasko e sempre que o texto dentro do UILabel era maior do que o UILabel podia conter, o texto começava a se mover para baixo em vez de ficar alinhado na parte superior.

Alterei esta linha para garantir que o texto esteja alinhado corretamente

    [super drawTextInRect:CGRectMake(0, 0, ceilf(CGRectGetWidth(self.frame)),MIN(ceilf(labelStringSize.height), self.frame.size.height))];

Answer #7

Você faria isso removendo a altura mínima.

Se você precisar da altura mínima para outra coisa abaixo do rótulo, use uma exibição de contêiner redimensionada com base no conteúdo da etiqueta, mas que use um mínimo.


Answer #8

Você pode usar o UITextView em vez do UILabel:
Desmarque a opção "Rolagem ativada"
Desmarque "Editável"
Desmarque "Selecionável"
Defina a cor de fundo como ClearColor


Answer #9

Editar

Na minha resposta original, eu estava usando o estilo de parágrafo do rótulo. Acontece que, para rótulos de várias linhas, isso realmente impede que o rótulo seja de várias linhas. Como resultado, removi-o do cálculo. Veja mais sobre isso no Github

Para aqueles que estão mais à vontade com o uso de código aberto, consulte TTTAttributedLabel onde você pode definir o alinhamento do texto do rótulo como TTTAttributedLabelVerticalAlignmentTop

O truque é subclassificar UILabel e substituir drawTextInRect . Em seguida, imponha que o texto seja desenhado na origem dos limites do rótulo.

Aqui está uma implementação ingênua que você pode usar agora:

Rápido

@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
    }
}

Objetivo-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

Desde que eu usei o IBDesignable, você pode adicionar esse rótulo a um storyboard e assistir, é assim que me parece


Answer #10
  @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()
      }
    }
  }




uilabel