web-dev-qa-db-fra.com

Comment définir l'alignement en haut à gauche de l'application UILabel pour iOS?

J'ai ajouté une étiquette dans mon fichier nib, puis il est nécessaire d'avoir un alignement en haut à gauche pour cette étiquette. Comme je fournis du texte au moment de l’exécution, il n’est donc pas sûr de savoir combien il y a de lignes . Donc, si le texte ne contient qu’une seule ligne, il apparaît alors comme aligné verticalement au centre. Cet alignement ne correspond pas à mon label respectif devant lui.

Par exemple:

enter image description here

Qui cherche étrange :(

Y at-il un moyen où je peux définir le texte de l'étiquette propre à l'alignement en haut à gauche?

84
Mrunal

Plutôt que d’expliquer à nouveau, je vais vous renvoyer à cette question/réponse plutôt détaillée et hautement cotée:

Aligner verticalement le texte en haut dans un UILabel

La réponse courte est non, Apple n'a pas rendu cela facile, mais c'est possible en modifiant la taille du cadre.

58
shawnwall

C'est assez facile à faire. Créez une sous-classe UILabel avec une propriété verticalAlignment et remplacez textRectForBounds:limitedToNumberOfLines pour renvoyer les limites correctes d'un alignement vertical supérieur, central ou inférieur. Voici le code:

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
63
memmons

La SOLabel travaille pour moi.

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:

Cette version a été mise à jour depuis l'original pour permettre la prise en charge des langues 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)
    }
}
37
totiG

J'ai trouvé une solution utilisant AutoLayout dans StoryBoard.

1) Définissez le nombre de lignes sur 0 et l’alignement du texte sur Gauche.

 enter image description here

2) Définir la contrainte de hauteur.

 enter image description here

3) La contrainte de hauteur doit être en relation - inférieure ou égale

 enter image description here

4) 

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

J'ai eu le résultat comme suit: 

 enter image description here

33
A.G

Dans votre code

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

Attention, si vous utilisez cela dans des cellules de tableau ou d'autres vues recyclées avec des données différentes, vous devez stocker le cadre d'origine quelque part et le réinitialiser avant d'appeler sizeToFit.

11
n13

Je rencontrais également ce problème, mais ce que j’ai trouvé, c’est l’ordre dans lequel vous définissez les propriétés et les méthodes d’UILabel qui compte!

Si vous appelez [label sizeToFit] avant label.font = [UIFont fontWithName:@"Helvetica" size:14];, le texte ne sera pas aligné en haut, mais si vous le permutez, il le fera!

J'ai également remarqué que le réglage du texte en premier fait également une différence.

J'espère que cela t'aides.

6
rharvey

J'ai trouvé une autre solution pour le même problème. J'ai utilisé UITextView au lieu de UILabel et ai changé la fonction editable() en false.

6
Maxim Zh

Lorsque vous utilisez le générateur d'interface, définissez les contraintes pour votre étiquette (veillez également à définir la hauteur et la largeur). Ensuite, dans l'inspecteur de taille, vérifiez la hauteur de l'étiquette. Là, vous voudrez qu'il lise> = au lieu de =. Ensuite, dans l'implémentation de ce contrôleur de vue, définissez le nombre de lignes sur 0 (vous pouvez également le faire dans IB) et définissez le libellé [label sizeToFit]; et à mesure que votre texte gagne en longueur, l'étiquette grandira en hauteur et conservera votre texte en haut à gauche. 

4
TomG103

La solution avec SoLabel fonctionne, merci.

J'ai ajouté la version monotouch ci-dessous:

    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
plvlad

En me basant sur la réponse impressionnante de totiG, j'ai créé une classe IBDesignable qui permet de personnaliser très facilement l'alignement vertical d'un UILabel à partir du StoryBoard. Assurez-vous simplement que vous avez défini la classe de votre UILabel sur 'VerticalAlignLabel' à partir de l'inspecteur d'identité StoryBoard. Si l'alignement vertical ne prend pas effet, accédez à Editeur -> Actualiser toutes les vues, ce qui devrait suffire. 

Comment ça marche: Une fois que vous avez défini la classe de votre UILabel correctement, le storyboard devrait vous montrer un champ de saisie prenant un entier (code d'alignement).

Mise à jour: j'ai ajouté le support pour les étiquettes centrées ~ Sev


Entrez 0 pour l'alignement supérieur

Entrez 1 pour l'alignement moyen

Entrez 2 pour l'alignement du bas

    @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
Nino Bouchedid

Si ce dont vous avez besoin est un texte non modifiable qui commence par défaut dans le coin supérieur gauche, vous pouvez simplement utiliser une vue texte au lieu d'une étiquette, puis définir son état sur non modifiable, comme ceci:

textview.isEditable = false

Bien plus facile que de jouer avec les étiquettes ...

À votre santé!

2
Alejandro Camus

Le moyen le plus simple et le plus simple consiste à incorporer Label dans StackView et à définir Axis de StackView sur Horizontal, Alignement sur le haut dans l'Inspecteur des attributs de Storyboard comme illustré ici .

2
Baig

Dans mon cas, c’était un problème de contrainte bottom space. Je l'avais mis à = 16.

Lorsque je l'ai défini sur bottom to >= 16, le problème a été résolu.

De plus, si l'étiquette contient une contrainte de hauteur, vous devez la supprimer.

Voici la vue des contraintes de mon étiquette dans l'inspecteur de taille:

 constraint

2
Mukesh Chapagain

Swift 3 version de la réponse de @ 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
bz13

Swift 2.0: : Utilisation de l'extension UILabel

Définissez des valeurs d'énumération constantes dans un fichier Swift vide.

//  AppRef.Swift

import UIKit
import Foundation

enum UILabelTextPositions : String {

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

}

Utiliser l'extension UILabel:

Créez une classe Swift vide et nommez-la. Ajoutez ce qui suit.

//  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!

     }
    }

Utilisation:

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

Pour iOS 7 c'est ce que j'ai fait et travaillé pour moi 

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

vous pouvez aussi simplement changer votre UILabel en UITextView, car ils font essentiellement la même chose, à la différence que l'avantage de UITextView est que le texte est automatiquement aligné en haut à gauche. 

0
Ndamu

Comment définir l'alignement en haut à gauche de l'application UILabel pour iOS? Étiquette Définir le mode de contenu sur "Haut gauche" fonctionne pour moi, merci beaucoup
 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

0
mahasam