如何为iOS应用程序设置UILabel的左上对齐?


99

我在笔尖文件中添加了一个标签,然后要求该标签具有左上对齐。由于我在运行时提供文本,因此无法确定有多少行。因此,如果文本仅包含单行,则显示为垂直居中对齐。这种对齐方式与我前面的相应标签不匹配。

例如:

在此处输入图片说明

看起来很奇怪:(

有什么方法可以将标签文本设置为左上对齐?


Answers:



64

这很容易做到。创建UILabel具有verticalAlignment属性的sublcass 并覆盖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.m

@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

3
在运行该解决方案之前,我还在SO上尝试了许多其他解决方案。效果很好!请记住,如果要在StoryBoard中执行此操作,请确保将CustomClass属性设置为SOLabel(或任何您决定命名的属性),而不是UILabel(在实用工具检查器中)。
TMc 2014年

这非常有帮助,谢谢。它不适用于居中对齐或右对齐的文本,但是使用bounds.size.width代替rect.size.widthin textRectForBounds:limitedToNumberOfLines:似乎可以解决此问题。
Geoff Hackworth

1
如果您在iOS 9 Xcode 7中遇到了``线程1:EXC_BAD_ACCESS(代码2,地址= 0x ...)'',只需删除setter和getter-(VerticalAlignment)verticalAlignment; 和-(void)setVerticalAlignment:(VerticalAlignment)值函数,因为变量为@property。它是综合的,包含访问器。
felixwcf '16

我在这里在方法中做了一些修改:“ textRectForBounds”-结果= CGRectMake(rect.origin.x,bounds.origin.y,rect.size.width,rect.size.height); 为了使我的作品对rightAlignment可用。
g212gs '16

50

我在StoryBoard中找到了使用AutoLayout的解决方案。

1)将行数设置为0,并将文本对齐设置为“左”。

在此处输入图片说明

2)设置高度限制。

在此处输入图片说明

3)高度限制应为关系式-小于或等于

在此处输入图片说明

4)

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

我得到的结果如下:

在此处输入图片说明


2
即使在具有重复使用的UITableViewCell中,其工作方式也很吸引人。
alex.bour

您将放置viewWillLayoutSubviews在控制器还是单元文件中?如果是控制器,它将如何从单元访问UILabel?
Craig.Pearce

您将步骤4放在哪里?作为一个新用户,我很高兴拥有一个纯粹的UI解决方案,然后代码无处不在,我们也没有被告知放置在哪里
velkoon

在SampleClass.swift或SampleTableViewCell.swift上
AG

这应该是解决方案。完美运行,无需黑客或子类化。
Curious101 '18

44

SOLabel为我工作。

Swift 3和5:

此版本已从原始版本进行了更新,以允许支持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)
    }
}

斯威夫特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)
    }
}

如果我尝试使用以下代码创建标签:var myLabel = VerticalAlignLabel()我收到“调用中参数'coder'的缺少参数”。如何使用这个VerticalAlignLabel子类创建标签?
RanLearn '17

1
立即尝试Swift版本3-我有一个不需要的初始化。
totiG

14

就我而言,这是bottom space约束问题。我已经设定为= 16

当我设置为 bottom to >= 16,此问题已解决。

另外,如果标签中有任何高度限制,则需要将其删除。

这是尺寸检查器中我标签的约束视图:

约束


选择标签时,没有约束选项。
velkoon '18 -10-6

最简单的修复-让约束和自动布局处理它。谢谢!
杰森

13

在你的代码中

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

请注意,如果您在用不同数据回收的表单元格或其他视图中使用该代码,则需要在调用sizeToFit之前将原始帧存储在某个地方并重置它。


我建议此时实际上将所有内容留给自动排版。这不再需要了。
n13

9

我发现了相同问题的另一种解决方案。我用UITextView代替UILabel并将editable()功能切换到false


@geekyaleks为什么这是一个愚蠢的hack?似乎是一个不错的解决方法,除了不能直接回答问题以外,还有其他问题吗?
Christopher Larsen

这是不合适的,因为您没有为作业使用适当的UI组件。对于诸如垂直对齐之类的简单操作,这不应是一个折衷方案。需要为工作使用正确的组件。还有什么是黑客...
geekyaleks

7

我也遇到了这个问题,但是我发现设置UILabel属性和方法的顺序很重要!

如果您[label sizeToFit]之前致电label.font = [UIFont fontWithName:@"Helvetica" size:14];则文本不会与顶部对齐,但是如果您将它们交换到顶部,则可以!

我还注意到,首先设置文本也会有所不同。

希望这可以帮助。


大。最后应调用sizeToFit()。
MKatleast3

4

在使用界面生成器时,请设置标签的约束(请确保同时设置高度和宽度)。然后在尺寸检查器中,检查标签的高度。在那里,您将希望它读取> =而不是=。然后,在该视图控制器的实现中,将行数设置为0(也可以在IB中完成),并设置标签[label sizeToFit];随着文本长度的增加,标签的高度会增加,并且文本会保持在左上方。


4

如果您需要的是默认情况下从左上角开始的不可编辑文本,则可以简单地使用文本视图而不是标签,然后将其状态设置为不可编辑,如下所示:

textview.isEditable = false

比弄乱标签更容易...

干杯!


3

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
}

3

最简单,最简单的方法是将Label嵌入StackView中,然后从Storyboard的Attribute Inspector中将StackView的Axis设置为Horizo​​ntal,将Alignment设置为Top,如此处所示


2

在totiG出色的答案的基础上,我创建了一个IBDesignable类,使从StoryBoard直接定制UILabel的垂直对齐方式变得非常容易。只需确保从StoryBoard身份检查器将UILabel的类设置为“ VerticalAlignLabel”即可。如果垂直对齐不生效,请转到“编辑器”->“刷新所有视图”,该方法可以解决问题。

工作原理:正确设置UILabel的类后,情节提要板将向您显示一个采用整数(对齐代码)的输入字段。

更新:我添加了对居中标签的支持〜Sev


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


2

您也可以只将UILabel更改为UITextView,因为它们基本上可以做相同的事情,除了UITextView的优点是文本自动对齐到左上角


1

我遇到了这个问题,但是我的标签位于UITableViewCell中,并且解决该问题的最简单方法是创建一个空的UIView并将其内部的标签设置为仅在顶部和左侧具有约束,而不受诅咒将行数设置为0


0

对于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];
}

0

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)

您能解释一下需要sampleLabel: UILabel?什么吗?
Craig.Pearce'December

在此函数makeLabelTextPosition(sampleLabel:UILabel ?, positionIdentifier:String){}上,您必须传递UILabel对象。
AG

0

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

0

@totiG的答案是正确的,并解决了我的问题。但是我在实施这种方法时发现了一个问题,在像5s,SE这样的小型设备中,这对我不起作用。我必须设置label.sizeToFit()override func layoutSubViews()

override func layoutSubViews() {
    super.layoutSubViews()
    // Do other works if needed
    label.sizeToFit()
}

0

迅捷5

很简单,属性的顺序就是一切。

titleLabel.frame = CGRect(x: 20, y: 20, width: 374, height: 291.2)
titleLabel.backgroundColor = UIColor.clear //set a light color to see the frame
titleLabel.textAlignment = .left
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.numberOfLines = 4
titleLabel.font = UIFont(name: "HelveticaNeue-Bold", size: 35)
titleLabel.text = "Example"
titleLabel.sizeToFit()
self.view.addSubview(titleLabel)

-2

如何为iOS应用程序设置UILabel的左上对齐?将标签内容模式设置为“左上”对我有用,非常感谢:
如何为iOS应用程序设置UILabel的左上对齐? 将标签内容模式设置为“左上”对我有用,非常感谢


1
绝对不为我做任何事。从直觉上看,这似乎应该是解决方案,这就是为什么当它不起作用时我转向Google的原因(或者看起来像是千方百计)。
velkoon '18 -10-6
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.