Как установить выравнивание сверху влево для приложения UILabel для iOS?


Я добавил одну метку в свой файл nib, а затем ее требуется выровнять по левому краю для этой метки. Поскольку я предоставляю текст во время выполнения, поэтому он не уверен, сколько строк есть. Поэтому, если текст содержит только одну строку, он отображается как выровненный по вертикали. Это выравнивание не соответствует моему соответствующему ярлыку перед ним.

например:

что выглядит странно : (

есть ли способ, где я могу установить метки текст, соответствующий выравниванию сверху влево?

17 76

17 ответов:

вместо того, чтобы заново объяснять, я свяжусь с этим довольно обширным и высоко оцененным вопросом / ответом:

вертикально выровнять текст сверху в пределах UILabel

короткий ответ-нет, Apple не сделала это легко, но это возможно, изменив размер кадра.

Это довольно легко сделать. Создайте UILabel sublcass с verticalAlignment свойства и переопределить textRectForBounds:limitedToNumberOfLines чтобы вернуть правильные границы для верхнего, среднего или нижнего вертикального выравнивания. Вот код:

SOLabel.h

#import <UIKit/UIKit.h>

typedef enum
{
    VerticalAlignmentTop = 0, // default
    VerticalAlignmentMiddle,
    VerticalAlignmentBottom,
} VerticalAlignment;

@interface SOLabel : UILabel

   @property (nonatomic, readwrite) VerticalAlignment verticalAlignment;

@end

SOLabel.м

@implementation SOLabel

-(id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (!self) return nil;

    // set inital value via IVAR so the setter isn't called
    _verticalAlignment = VerticalAlignmentTop;

    return self;
}

-(VerticalAlignment) verticalAlignment
{
    return _verticalAlignment;
}

-(void) setVerticalAlignment:(VerticalAlignment)value
{
    _verticalAlignment = value;
    [self setNeedsDisplay];
}

// align text block according to vertical alignment settings
-(CGRect)textRectForBounds:(CGRect)bounds 
    limitedToNumberOfLines:(NSInteger)numberOfLines
{
   CGRect rect = [super textRectForBounds:bounds 
                   limitedToNumberOfLines:numberOfLines];
    CGRect result;
    switch (_verticalAlignment)
    {
       case VerticalAlignmentTop:
          result = CGRectMake(bounds.origin.x, bounds.origin.y, 
                              rect.size.width, rect.size.height);
           break;

       case VerticalAlignmentMiddle:
          result = CGRectMake(bounds.origin.x, 
                    bounds.origin.y + (bounds.size.height - rect.size.height) / 2,
                    rect.size.width, rect.size.height);
          break;

       case VerticalAlignmentBottom:
          result = CGRectMake(bounds.origin.x, 
                    bounds.origin.y + (bounds.size.height - rect.size.height),
                    rect.size.width, rect.size.height);
          break;

       default:
          result = bounds;
          break;
    }
    return result;
}

-(void)drawTextInRect:(CGRect)rect
{
    CGRect r = [self textRectForBounds:rect 
                limitedToNumberOfLines:self.numberOfLines];
    [super drawTextInRect:r];
}

@end

SOLabel работает для меня.

Swift 1:

class UIVerticalAlignLabel: UILabel {

enum VerticalAlignment : Int {
    case VerticalAlignmentTop = 0
    case VerticalAlignmentMiddle = 1
    case VerticalAlignmentBottom = 2
}

var verticalAlignment : VerticalAlignment = .VerticalAlignmentTop {
    didSet {
        setNeedsDisplay()
    }
}

required init(coder aDecoder: NSCoder){
    super.init(coder: aDecoder)
}

override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines: Int) -> CGRect {
    let rect = super.textRectForBounds(bounds, limitedToNumberOfLines: limitedToNumberOfLines)

    switch(verticalAlignment) {
        case .VerticalAlignmentTop:
            return CGRectMake(bounds.origin.x, bounds.origin.y, rect.size.width, rect.size.height)
        case .VerticalAlignmentMiddle:
            return CGRectMake(bounds.origin.x, bounds.origin.y + (bounds.size.height - rect.size.height) / 2, rect.size.width, rect.size.height)
        case .VerticalAlignmentBottom:
            return CGRectMake(bounds.origin.x, bounds.origin.y + (bounds.size.height - rect.size.height), rect.size.width, rect.size.height)
        default:
            return bounds
    }
}

override func drawTextInRect(rect: CGRect) {
    let r = self.textRectForBounds(rect, limitedToNumberOfLines: self.numberOfLines)
    super.drawTextInRect(r)
    }
}

Swift 3:

эта версия была обновлена с оригинала, чтобы обеспечить поддержку языков RTL:

public class VerticalAlignLabel: UILabel {
    enum VerticalAlignment {
        case top
        case middle
        case bottom
    }

    var verticalAlignment : VerticalAlignment = .top {
        didSet {
            setNeedsDisplay()
        }
    }

    override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines: Int) -> CGRect {
        let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: limitedToNumberOfLines)

        if UIView.userInterfaceLayoutDirection(for: .unspecified) == .rightToLeft {
            switch verticalAlignment {
            case .top:
                return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
            case .middle:
                return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height)
            case .bottom:
                return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height)
            }
        } else {
            switch verticalAlignment {
            case .top:
                return CGRect(x: bounds.origin.x, y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
            case .middle:
                return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height)
            case .bottom:
                return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height)
            }
        }
    }

    override public func drawText(in rect: CGRect) {
        let r = self.textRect(forBounds: rect, limitedToNumberOfLines: self.numberOfLines)
        super.drawText(in: r)
    }
}

Я нашел решение с помощью AutoLayout в раскадровке.

1) Установите нет строк в 0 и выравнивание текста влево.

enter image description here

2) Установите ограничение по высоте.

enter image description here

3) ограничение высоты должно быть по отношению-меньше или равно

enter image description here

4)

   override func viewWillLayoutSubviews() {
        sampleLabel.sizeToFit()
    }

Я получил результат следующим образом :

enter image description here

код

label.text = @"some text";
[label sizeToFit];

помните, что если вы используете это в ячейках таблицы или других представлениях, которые перерабатываются с другими данными, вам нужно будет сохранить исходный кадр где-то и сбросить его перед вызовом sizeToFit.

у меня также была эта проблема, но я обнаружил, что порядок, в котором вы устанавливаете свойства и методы UILabel, имеет значение!

Если вы называете [label sizeToFit] до label.font = [UIFont fontWithName:@"Helvetica" size:14]; тогда текст не выравнивается по верху, но если вы меняете их вокруг, то это делает!

Я также заметил, что установка текста сначала тоже имеет значение.

надеюсь, что это помогает.

Я нашел другое решение для той же проблемы. Я использовал UITextView вместо UILabel и перешли

при использовании построителя интерфейса установите ограничения для вашей метки (не забудьте также установить высоту и ширину). Затем в Инспекторе размеров проверьте высоту метки. Там вы хотите, чтобы он читал >= вместо =. Затем в реализации для этого контроллера вида установите количество строк в 0 (также можно сделать в IB) и установите метку [label sizeToFit]; и по мере увеличения длины текста метка будет расти в высоту и сохранять текст в верхнем левом углу.

решение с SoLabel работает, Спасибо.

ниже я добавил версию monotouch:

    public class UICustomLabel : UILabel
{
    private UITextVerticalAlignment _textVerticalAlignment;

    public UICustomLabel()
    {
        TextVerticalAlignment = UITextVerticalAlignment.Top;
    }

    public UITextVerticalAlignment TextVerticalAlignment
    {
        get
        {
            return _textVerticalAlignment;
        }
        set
        {
            _textVerticalAlignment = value;
            SetNeedsDisplay();
        }
    }

    public override void DrawText(RectangleF rect)
    {
        var bound = TextRectForBounds(rect, Lines);
        base.DrawText(bound);
    }

    public override RectangleF TextRectForBounds(RectangleF bounds, int numberOfLines)
    {
        var rect = base.TextRectForBounds(bounds, numberOfLines);
        RectangleF resultRect;
        switch (TextVerticalAlignment)
        {
            case UITextVerticalAlignment.Top:
                resultRect = new RectangleF(bounds.X, bounds.Y, rect.Size.Width, rect.Size.Height);
                break;
            case UITextVerticalAlignment.Middle:
                resultRect = new RectangleF(bounds.X,
                                            bounds.Y + (bounds.Size.Height - rect.Size.Height)/2,
                                            rect.Size.Width, rect.Size.Height);
                break;
            case UITextVerticalAlignment.Bottom:
                resultRect = new RectangleF(bounds.X,
                                            bounds.Y + (bounds.Size.Height - rect.Size.Height),
                                            rect.Size.Width, rect.Size.Height);
                break;

            default:
                resultRect = bounds;
                break;
        }

        return resultRect;
    }
}

public enum UITextVerticalAlignment
{
    Top = 0, // default
    Middle,
    Bottom
}

самый простой и легкий способ-встроить метку в StackView и установить ось StackView в горизонтальное положение, выравнивание сверху в Инспекторе атрибутов из раскадровки как показано здесь.

Если вам нужен не редактируемый текст, который по умолчанию начинается в верхнем левом углу, вы можете просто использовать текстовое представление вместо метки, а затем установить его состояние в не редактируемое, например:

textview.isEditable = false

намного проще, чем возиться с ярлыками...

Ура!

основываясь на удивительном ответе totiG, я создал класс IBDesignable, который позволяет очень легко настроить вертикальное выравнивание UILabel прямо с раскадровки. Просто убедитесь, что вы установили класс UILabel в "VerticalAlignLabel" из инспектора идентификации раскадровки. Если вертикальное выравнивание не вступает в силу, перейдите в Редактор->обновить все виды, которые должны сделать трюк.

Как это работает: Как только вы правильно установите класс UILabel, раскадровка должна показать вам поле ввода, которое принимает целое число (код выравнивания).

обновление: я добавил поддержку центрированных меток ~сев


введите 0 для верхнего выравнивания

введите 1 для среднего выравнивания

введите 2 для выравнивания дна

    @IBDesignable class VerticalAlignLabel: UILabel {
    
    @IBInspectable var alignmentCode: Int = 0 {
        didSet {
            applyAlignmentCode()
        }
    }
    
    func applyAlignmentCode() {
        switch alignmentCode {
        case 0:
            verticalAlignment = .top
        case 1:
            verticalAlignment = .topcenter
        case 2:
            verticalAlignment = .middle
        case 3:
            verticalAlignment = .bottom
        default:
            break
        }
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
        self.applyAlignmentCode()
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        
        self.applyAlignmentCode()
    }
    
    enum VerticalAlignment {
        case top
        case topcenter
        case middle
        case bottom
    }
    
    var verticalAlignment : VerticalAlignment = .top {
        didSet {
            setNeedsDisplay()
        }
    }
    
    override public func textRect(forBounds bounds: CGRect, limitedToNumberOfLines: Int) -> CGRect {
        let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: limitedToNumberOfLines)
        
        if #available(iOS 9.0, *) {
            if UIView.userInterfaceLayoutDirection(for: .unspecified) == .rightToLeft {
                switch verticalAlignment {
                case .top:
                    return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
                case .topcenter:
                    return CGRect(x: self.bounds.size.width - (rect.size.width / 2), y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
                case .middle:
                    return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height)
                case .bottom:
                    return CGRect(x: self.bounds.size.width - rect.size.width, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height)
                }
            } else {
                switch verticalAlignment {
                case .top:
                    return CGRect(x: bounds.origin.x, y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
                case .topcenter:
                    return CGRect(x: (self.bounds.size.width / 2 ) - (rect.size.width / 2), y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
                case .middle:
                    return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height)
                case .bottom:
                    return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height)
                }
            }
        } else {
            // Fallback on earlier versions
            return rect
        }
    }
    
    override public func drawText(in rect: CGRect) {
        let r = self.textRect(forBounds: rect, limitedToNumberOfLines: self.numberOfLines)
        super.drawText(in: r)
    }
}

В моем случае, это был bottom space проблема ограничения. Я установил его в = 16.

когда я установил его в bottom to >= 16, этот вопрос был решен.

кроме того, если у вас есть какие-либо ограничения по высоте в метке, то вам нужно удалить его.

вот вид ограничения моей метки в инспекторе размеров:

constraint

для iOS 7 это то, что я сделал и работал для меня

@implementation UILabel (VerticalAlign)
- (void)alignTop
{
    CGSize boundingRectSize = CGSizeMake(self.frame.size.width, CGFLOAT_MAX);
    NSDictionary *attributes = @{NSFontAttributeName : self.font};
    CGRect labelSize = [self.text boundingRectWithSize:boundingRectSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                              attributes:attributes
                                                 context:nil];
    int numberOfLines= ceil(labelSize.size.height / self.font.lineHeight);

    CGRect newFrame = self.frame;
    newFrame.size.height = numberOfLines * self.font.lineHeight;
    self.frame = newFrame;
}

- (void)alignBottom
{
    CGSize boundingRectSize = CGSizeMake(self.frame.size.width, CGFLOAT_MAX);
    NSDictionary *attributes = @{NSFontAttributeName : self.font};
    CGRect labelSize = [self.text boundingRectWithSize:boundingRectSize options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading
                                            attributes:attributes
                                               context:nil];
    int numberOfLines= ceil(labelSize.size.height / self.font.lineHeight);

    int numberOfNewLined = (self.frame.size.height/self.font.lineHeight) - numberOfLines;

    NSMutableString *newLines = [NSMutableString string];
    for(int i=0; i< numberOfNewLined; i++){
        [newLines appendString:@"\n"];
    }
    [newLines appendString:self.text];
    self.text = [newLines mutableCopy];
}

Swift 2.0:: Использование Расширения UILabel

сделайте постоянные значения перечисления в пустом файле Swift.

//  AppRef.swift

import UIKit
import Foundation

enum UILabelTextPositions : String {

 case VERTICAL_ALIGNMENT_TOP = "VerticalAlignmentTop"
 case VERTICAL_ALIGNMENT_MIDDLE = "VerticalAlignmentMiddle"
 case VERTICAL_ALIGNMENT_BOTTOM = "VerticalAlignmentBottom"

}

Используя Расширение UILabel:

создайте пустой класс Swift и назовите его. Добавить следующее.

//  AppExtensions.swift

import Foundation
import UIKit

    extension UILabel{ 
     func makeLabelTextPosition (sampleLabel :UILabel?, positionIdentifier : String) -> UILabel
     {
      let rect = sampleLabel!.textRectForBounds(bounds, limitedToNumberOfLines: 0)

      switch positionIdentifier
      {
      case "VerticalAlignmentTop":
       sampleLabel!.frame = CGRectMake(bounds.origin.x+5, bounds.origin.y, rect.size.width, rect.size.height)
       break;

      case "VerticalAlignmentMiddle":
       sampleLabel!.frame = CGRectMake(bounds.origin.x+5,bounds.origin.y + (bounds.size.height - rect.size.height) / 2,
        rect.size.width, rect.size.height);
       break;

      case "VerticalAlignmentBottom":
       sampleLabel!.frame = CGRectMake(bounds.origin.x+5, bounds.origin.y + (bounds.size.height - rect.size.height),rect.size.width, rect.size.height);
       break;

      default:
       sampleLabel!.frame = bounds;
       break;
      }
      return sampleLabel!

     }
    }

использование :

myMessageLabel.makeLabelTextPosition(messageLabel, positionIdentifier: UILabelTextPositions.VERTICAL_ALIGNMENT_TOP.rawValue)

Swift 3 версия ответа @totiG

class UIVerticalAlignLabel: UILabel {
    enum VerticalAlignment : Int {
        case VerticalAlignmentTop = 0
        case VerticalAlignmentMiddle = 1
        case VerticalAlignmentBottom = 2
    }

    @IBInspectable var verticalAlignment : VerticalAlignment = .VerticalAlignmentTop {
        didSet {
            setNeedsDisplay()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines: Int) -> CGRect {
        let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: limitedToNumberOfLines)

        switch(verticalAlignment) {
        case .VerticalAlignmentTop:
            return CGRect(x: bounds.origin.x, y: bounds.origin.y, width: rect.size.width, height: rect.size.height)
        case .VerticalAlignmentMiddle:
            return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height) / 2, width: rect.size.width, height: rect.size.height)
        case .VerticalAlignmentBottom:
            return CGRect(x: bounds.origin.x, y: bounds.origin.y + (bounds.size.height - rect.size.height), width: rect.size.width, height: rect.size.height)
        }
    }

    override func drawText(in rect: CGRect) {
        let r = self.textRect(forBounds: rect, limitedToNumberOfLines: self.numberOfLines)
        super.drawText(in: r)
    }
}

Как установить выравнивание сверху влево для приложения UILabel для iOS? Метки установленный режим содержания в "верхнем левом углу" работа для меня, большое спасибо:
How to set top-left alignment for UILabel for iOS application? Label Set Content Mode to "Top Left" work for me, thank you very much