Как установить выравнивание сверху влево для приложения UILabel для iOS?
Я добавил одну метку в свой файл nib, а затем ее требуется выровнять по левому краю для этой метки. Поскольку я предоставляю текст во время выполнения, поэтому он не уверен, сколько строк есть. Поэтому, если текст содержит только одну строку, он отображается как выровненный по вертикали. Это выравнивание не соответствует моему соответствующему ярлыку перед ним.
например:
что выглядит странно : (
есть ли способ, где я могу установить метки текст, соответствующий выравниванию сверху влево?
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 и выравнивание текста влево.
2) Установите ограничение по высоте.
3) ограничение высоты должно быть по отношению-меньше или равно
4)
override func viewWillLayoutSubviews() { sampleLabel.sizeToFit() }
Я получил результат следующим образом :
код
label.text = @"some text"; [label sizeToFit];
помните, что если вы используете это в ячейках таблицы или других представлениях, которые перерабатываются с другими данными, вам нужно будет сохранить исходный кадр где-то и сбросить его перед вызовом sizeToFit.
у меня также была эта проблема, но я обнаружил, что порядок, в котором вы устанавливаете свойства и методы UILabel, имеет значение!
Если вы называете
[label sizeToFit]
доlabel.font = [UIFont fontWithName:@"Helvetica" size:14];
тогда текст не выравнивается по верху, но если вы меняете их вокруг, то это делает!Я также заметил, что установка текста сначала тоже имеет значение.
надеюсь, что это помогает.
при использовании построителя интерфейса установите ограничения для вашей метки (не забудьте также установить высоту и ширину). Затем в Инспекторе размеров проверьте высоту метки. Там вы хотите, чтобы он читал >= вместо =. Затем в реализации для этого контроллера вида установите количество строк в 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) } }
для 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) } }