Выровнять текст по вертикали внутри UILabel - PullRequest
2098 голосов
/ 28 июня 2009

У меня есть UILabel с пробелом для двух строк текста. Иногда, когда текст слишком короткий, этот текст отображается в вертикальном центре метки.

Как мне выровнять текст по вертикали, чтобы всегда быть в верхней части UILabel?

image representing a UILabel with vertically-centered text

Ответы [ 48 ]

6 голосов
/ 13 декабря 2013

Я знаю, что это старый пост, но выравнивание текста по вертикали - ОГРОМНАЯ проблема (по крайней мере, для меня), и я решил, что должен поделиться этим решением, так как сам не смог его найти.

Использование drawRect немного дорого, на мой взгляд. Правильный способ заставить UILabel выровнять по вертикали - не использовать UILabel. Используйте UITextView (многострочное UITextField) и наблюдайте за свойством содержимого так:

- (UITextView*)textView{
     if(!_textView){
        UIEdgeInsets insets = UIEdgeInsetsMake(0, 50, 0, 5);
        CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
        _textView = [[UITextView alloc]initWithFrame:UIEdgeInsetsInsetRect(frame, insets)];
        _textView.delegate = self;
        _textView.scrollEnabled = YES;
        _textView.bounces = YES;
        _textView.backgroundColor = [UIColor whiteColor];
        [_textView setUserInteractionEnabled:NO];
        [_textView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
    }
    return _textView;
}

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:    (NSDictionary *)change context:(void *)context {
UITextView *tv = object;

CGFloat height = [tv bounds].size.height;
CGFloat contentheight;

#ifdef __IPHONE_7_0
    contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;
#else
    contentheight = [tv contentSize].height;
#endif

    switch(self.verticalAlignment) {
        case VerticalAlignmentBottom:{
            CGFloat topCorrect = ([tv bounds].size.height - contentheight);
            topCorrect = (topCorrect <0.0 ? 0.0 : topCorrect);
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
        break;
        case VerticalAlignmentMiddle:{
            CGFloat topCorrect = (height - contentheight * [tv zoomScale])/2.0;
            topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
            break;
        case VerticalAlignmentTop:{
            tv.contentOffset = (CGPoint){.x = 0, .y = 0 };
        }
            break;
        default:
            break;
    }
}

В основном, что здесь происходит, мы устанавливаем класс, в котором мы находимся, в качестве наблюдателя, просматривая свойство contentSize с параметром NSKeyValueObservingOptionNew, чтобы при каждом изменении содержимого вызывался -(void)observeValueForKeyPath:ofObject:change:context:, а затем вы могли вычислить смещение размер, чтобы выровнять текст соответствующим образом.

Я не могу взять кредит на себя, оригинальная идея пришла от здесь . Но это решение не работает на iOS7. Пройдя несколько часов по SO, я обнаружил следующее: Исправление вертикального выравнивания iOS 7 . Ключевая строка там contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;. По какой-то причине в iOS 7 получение высоты contentSize не работает, но это исправляет. Ни одно из двух решений не сработало само по себе, но после небольшого изменения я смог объединить вышеприведенное решение.

6 голосов
/ 17 августа 2011

Если вы можете создать свой собственный вид, вы можете сделать что-то вроде этого:

- (void)drawRect:(CGRect)rect
{
    CGRect bounds = self.bounds;
    [self.textColor set];
    [self.text drawInRect:bounds
                 withFont:self.font
            lineBreakMode:UILineBreakModeTailTruncation
                alignment:self.textAlignment];
}
6 голосов
/ 16 мая 2012

В UILabel вертикальное выравнивание текста невозможно. Но вы можете динамически изменить высоту метки, используя метод sizeWithFont:, равный NSString, и просто установить его x и y, как вам нужно.

Вы можете использовать UITextField. Он поддерживает contentVerticalAlignment peoperty , поскольку это подкласс UIControl. Вы должны установить для userInteractionEnabled значение NO, чтобы пользователь не мог набирать на нем текст.

6 голосов
/ 29 марта 2016

Swift 2.0:

Сделать постоянные значения перечисления в пустом файле 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)
5 голосов
/ 10 апреля 2012

Я также работал над этой конкретной проблемой, поэтому я взял идеи DS и Неван Кинг и в основном объединил их в подкласс, который реализует свойство вертикального выравнивания , что также позволяет менять выравнивание более одного раза. Он занимает тип UIControlContentVerticalAlignment и также поддерживает UIControlContentVerticalAlignmentFill.

Из того, что я видел, numberOfLines кажется бесполезным, когда речь идет о вертикальном выравнивании, поэтому в этом подклассе он всегда равен 0 при применении вертикального выравнивания. Кроме того, вам все равно придется самостоятельно установить lineBreakMode на случай, если вам понадобится многострочная текстовая метка.

Вот оно: QALabel на GitHub

5 голосов
/ 15 декабря 2010

Я перебил предложение Далекинга и добавил UIEdgeInset, чтобы учесть настраиваемый запас. хорошая работа вокруг.

- (id)init
{
    if (self = [super init]) {
        contentEdgeInsets = UIEdgeInsetsZero;
    }

    return self;
}

- (void)layoutSubviews
{
    CGRect localBounds = self.bounds;
    localBounds = CGRectMake(MAX(0, localBounds.origin.x + contentEdgeInsets.left), 
                             MAX(0, localBounds.origin.y + contentEdgeInsets.top), 
                             MIN(localBounds.size.width, localBounds.size.width - (contentEdgeInsets.left + contentEdgeInsets.right)), 
                             MIN(localBounds.size.height, localBounds.size.height - (contentEdgeInsets.top + contentEdgeInsets.bottom)));

    for (UIView *subview in self.subviews) {
        if ([subview isKindOfClass:[UILabel class]]) {
            UILabel *label = (UILabel*)subview;
            CGSize lineSize = [label.text sizeWithFont:label.font];
            CGSize sizeForText = [label.text sizeWithFont:label.font constrainedToSize:localBounds.size lineBreakMode:label.lineBreakMode];

            NSInteger numberOfLines = ceilf(sizeForText.height/lineSize.height);

            label.numberOfLines = numberOfLines;
            label.frame = CGRectMake(MAX(0, contentEdgeInsets.left), MAX(0, contentEdgeInsets.top), localBounds.size.width, MIN(localBounds.size.height, lineSize.height * numberOfLines)); 
        }
    }
}
5 голосов
/ 25 октября 2016

для 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
    }
}
4 голосов
/ 14 июня 2016

Для тех из вас, у которых пользовательские ячейки таблицы пытаются решить эту проблему, добавьте это в класс пользовательских ячеек таблицы:

Swift 2.2:

override func layoutSubviews() {
    labelName.sizeToFit()
}

Это решило мою проблему.

4 голосов
/ 23 июля 2013
yourLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
4 голосов
/ 02 октября 2012

Существует два способа решения этой проблемы. Один из них

[mylabel setNumberOfLines:0];
[mylabel sizeToFit];

Но второй способ более надежен для этого подхода, т. Е.

 CGSize sizeToFit = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
 [mylabel setFrame:CGRectMake(mylabel.frame.origin.x, mylabel.frame.origin.y, sizeToFit.width, sizeToFit.height)];

ввод "\ n" не очень хорошая вещь, но да, если вы знаете ограничения и размер данных, которые будут отображаться, может быть, это работает хорошо, но не может быть расширен, если текст длиннее размера метки , Второй способ в конечном итоге установить рамку в соответствии с размером текста, который будет отображаться.

...