Souligner le texte dans UIlabel


89

Comment souligner un texte qui peut être composé de plusieurs lignes de chaîne? Je trouve que certaines personnes suggèrent UIWebView, mais c'est évidemment une classe trop lourde pour le rendu de texte.

Mes pensées étaient de déterminer le point de départ et la longueur de chaque chaîne dans chaque ligne. Et tracez une ligne en dessous en conséquence.

Je rencontre des problèmes pour déterminer la longueur et le point de départ de la chaîne.

J'ai essayé d'utiliser -[UILabel textRectForBounds:limitedToNumberOfLines:], cela devrait être le rectangle de délimitation du dessin pour le texte, n'est-ce pas? Alors je dois travailler sur l'alignement? Comment puis-je obtenir le point de départ de chaque ligne lorsqu'elle est justifiée au centre et à droite?


1
Regardez cet article de
blog

Réponses:


137

Vous pouvez sous-classer à partir de UILabel et remplacer la méthode drawRect:

- (void)drawRect:(CGRect)rect {
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSetRGBStrokeColor(ctx, 207.0f/255.0f, 91.0f/255.0f, 44.0f/255.0f, 1.0f); // RGBA
    CGContextSetLineWidth(ctx, 1.0f);

    CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
    CGContextAddLineToPoint(ctx, self.bounds.size.width, self.bounds.size.height - 1);

    CGContextStrokePath(ctx);

    [super drawRect:rect];  
}

UPD:
À partir d'iOS 6, Apple a ajouté la prise en charge de NSAttributedString pour UILabel, donc maintenant c'est beaucoup plus facile et fonctionne pour plusieurs lignes:

NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
myLabel.attributedText = [[NSAttributedString alloc] initWithString:@"Test string" 
                                                         attributes:underlineAttribute];

Si vous souhaitez toujours prendre en charge iOS 4 et iOS 5, je vous recommande d'utiliser TTTAttributedLabel plutôt que de souligner l'étiquette manuellement. Cependant, si vous devez souligner UILabel sur une ligne et ne voulez pas utiliser de composants tiers, le code ci-dessus fera toujours l'affaire.


3
Je suppose que cela ne dessinera qu'un seul soulignement pour la dernière ligne de chaîne, non? Qu'en est-il du soulignement de la chaîne dans les autres lignes?
semix

2
il ne fait pas plusieurs lignes, mais c'est le meilleur que je puisse trouver, donc je suppose que plusieurs lignes sont hors de question. Je suppose que la prochaine meilleure solution à laquelle je puisse penser est d'importer une police qui a un soulignement intégré dans la police. Cela ne fonctionnerait qu'à partir d'ios 4.0+ où vous pouvez importer des polices.
DonnaLea

salut, je veux savoir si cela enfreint l'une des normes ios ui.
thndrkiss

L'implémentation d'Apple (la deuxième suggestion) ne prend pas en charge les caractères qui vont en dessous de la ligne? screencast.com/t/NGvQJqoWAD3J
pfrank

Si nous utilisons le support NSAttributedString pour UILabel, pour les alphabets comme g, p & q, le soulignement est tronqué. Quelqu'un confronté au problème? Exemple: Connexion
dev4u

46

Dans Swift:

let underlineAttriString = NSAttributedString(string: "attriString",
                                          attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
label.attributedText = underlineAttriString

La seule chose que vous devez faire dans Swift 3 est de changer .StyleSingle en .styleSingle, c'est camelCased dans Swift3, mais bonne réponse!
Josh O'Connor

Sans .rawValue, cela provoquait un crash pour moi.
jackofallcode

vous n'auriez besoin que de .rawValue pour swift 4.0
carotzoe

Trop verbeux pour simplement dessiner un soulignement.
khcpietro

38

C'est ce que j'ai fait. Cela fonctionne comme du beurre.

1) Ajoutez CoreText.framework à vos Frameworks.

2) importez <CoreText / CoreText.h> dans la classe où vous avez besoin d'une étiquette soulignée.

3) Écrivez le code suivant.

    NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"My Messages"];
    [attString addAttribute:(NSString*)kCTUnderlineStyleAttributeName
              value:[NSNumber numberWithInt:kCTUnderlineStyleSingle]
              range:(NSRange){0,[attString length]}];
    self.myMsgLBL.attributedText = attString;
    self.myMsgLBL.textColor = [UIColor whiteColor];

+1 de ma part pour cette réponse, car cela fonctionne vraiment très bien, et cela montre un moyen facile de définir une plage de caractères spécifique également (ce dont j'avais besoin moi-même). Merci! - Erik
Erik van der Neut

19

Utilisez une chaîne d'attribut:

NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] initWithString:@"Your String"]
[attrString addAttribute:(NSString*)kCTUnderlineStyleAttributeName 
                   value:[NSNumber numberWithInt:kCTUnderlineStyleSingle] 
                   range:(NSRange){0,[attrString length]}];

Et puis remplacez l'étiquette - (void) drawTextInRect: (CGRect) aRect et restituer le texte dans quelque chose comme:

CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attrString);
drawingRect = self.bounds;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, drawingRect);
textFrame = CTFramesetterCreateFrame(framesetter,CFRangeMake(0,0), path, NULL);
CGPathRelease(path);
CFRelease(framesetter);
CTFrameDraw(textFrame, ctx);
CGContextRestoreGState(ctx);

Ou mieux encore, au lieu de remplacer, utilisez simplement le OHAttributedLabel créé par Olivier Halligon


1
La ligne du haut devrait êtreNSMutableAttributedString
borrrden

La raison pour laquelle j'ai abandonné OHAttributedLabel était que - du moins pour moi, il n'était pas possible de calculer une hauteur de texte précise. dans 10% des cas, il était incorrect. (peut-être parce que j'utilisais une police différente ..)
Guntis Treulands

15

J'ai combiné certaines des réponses fournies, pour créer une meilleure sous-classe UILabel (au moins pour mes besoins), qui prend en charge:

  • texte multiligne avec diverses limites d'étiquette (le texte peut être au milieu du cadre de l'étiquette ou à une taille précise)
  • souligner
  • barré
  • décalage de ligne de soulignement / barré
  • Alignement du texte
  • différentes tailles de police

https://github.com/GuntisTreulands/UnderLineLabel


11

Les personnes qui ne veulent pas sous-classer la vue (UILabel / UIButton) etc ... 'forgetButton' peut également être remplacé par n'importe quelle étiquette.

-(void) drawUnderlinedLabel {
    NSString *string = [forgetButton titleForState:UIControlStateNormal];
    CGSize stringSize = [string sizeWithFont:forgetButton.titleLabel.font];
    CGRect buttonFrame = forgetButton.frame;
    CGRect labelFrame = CGRectMake(buttonFrame.origin.x + buttonFrame.size.width - stringSize.width, 
            buttonFrame.origin.y + stringSize.height + 1 , 
            stringSize.width, 2);
    UILabel *lineLabel = [[UILabel alloc] initWithFrame:labelFrame];
    lineLabel.backgroundColor = [UIColor blackColor];
    //[forgetButton addSubview:lineLabel];
    [self.view addSubview:lineLabel];
}

2
-1 pour appeler "draw…" une méthode qui alloue un UILabel et l'ajoute à la vue.
jcayzac

1
J'ai adapté cela pour être un peu plus générique: pastebin.com/QkF9ifpb original ne tient pas compte du fait que l'étiquette est dans une sous-vue.
Fonix

8
NSString *tem =self.detailCustomerCRMCaseLabel.text;
if (tem != nil && ![tem isEqualToString:@""]) {
    NSMutableAttributedString *temString=[[NSMutableAttributedString alloc]initWithString:tem];
    [temString addAttribute:NSUnderlineStyleAttributeName
                      value:[NSNumber numberWithInt:1]
                      range:(NSRange){0,[temString length]}];
    self.detailCustomerCRMCaseLabel.attributedText = temString;
}

7

Une autre solution pourrait être (depuis iOS 7) une valeur négative à NSBaselineOffsetAttributeName, par exemple votre NSAttributedStringpourrait être:

NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:@"my text goes here'
                                                            attributes:@{NSFontAttributeName: [UIFont fontWithName:@"Helvetica-Regular" size:12],
                                                                         NSForegroundColorAttributeName: [UIColor blackColor],
                                                                         NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSBaselineOffsetAttributeName: @(-3)}];

J'espère que cela aidera ;-)


7
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;

3

Vous pouvez créer une étiquette personnalisée avec le nom UnderlinedLabel et modifier la fonction drawRect.

#import "UnderlinedLabel.h"

@implementation UnderlinedLabel

- (void)drawRect:(CGRect)rect
{
   NSString *normalTex = self.text;
   NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
   self.attributedText = [[NSAttributedString alloc] initWithString:normalTex
                                                      attributes:underlineAttribute];

   [super drawRect:rect];
}

3

Voici la solution la plus simple qui fonctionne pour moi sans écrire de codes supplémentaires.

// To underline text in UILable
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Type your text here"];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
lblText.attributedText = text;

3

Parfois, nous développeur coincé dans une petite partie de conception de n'importe quel écran d'interface utilisateur. L'une des exigences les plus irritantes est le texte en ligne. Ne vous inquiétez pas, voici la solution.

entrez la description de l'image ici

Souligner un texte dans un UILabel à l'aide de l'Objectif C

UILabel *label=[[UILabel alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
label.backgroundColor=[UIColor lightGrayColor];
NSMutableAttributedString *attributedString;
attributedString = [[NSMutableAttributedString alloc] initWithString:@"Apply Underlining"];
[attributedString addAttribute:NSUnderlineStyleAttributeName value:@1 range:NSMakeRange(0,
[attributedString length])];
[label setAttributedText:attributedString];

Souligner un texte dans UILabel à l'aide de Swift

 label.backgroundColor = .lightGray
 let attributedString = NSMutableAttributedString.init(string: "Apply UnderLining")
 attributedString.addAttribute(NSUnderlineStyleAttributeName, value: 1, range:
NSRange.init(location: 0, length: attributedString.length))
 label.attributedText = attributedString

1

Une version améliorée du code de Kovpas (couleur et taille de ligne)

@implementation UILabelUnderlined

- (void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

    CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA

    CGContextSetLineWidth(ctx, 1.0f);

    CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];

    CGContextMoveToPoint(ctx, 0, self.bounds.size.height - 1);
    CGContextAddLineToPoint(ctx, tmpSize.width, self.bounds.size.height - 1);

    CGContextStrokePath(ctx);

    [super drawRect:rect];  
}

@end

1

J'ai créé pour uilabel multiligne avec soulignement:

Pour les tailles de police 8 à 13, définissez int lineHeight = self.font.pointSize + 3;

Pour les tailles de police de 14 à 20, définissez int lineHeight = self.font.pointSize + 4;

- (void)drawRect:(CGRect)rect 

{

CGContextRef ctx = UIGraphicsGetCurrentContext();

const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA

CGContextSetLineWidth(ctx, 1.0f);
CGSize tmpSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(self.frame.size.width, 9999)];

int height = tmpSize.height;

int lineHeight = self.font.pointSize+4;    

int maxCount = height/lineHeight;

float totalWidth = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(1000, 9999)].width;

for(int i=1;i<=maxCount;i++)

{

    float width=0.0;
    if((i*self.frame.size.width-totalWidth)<=0)
        width = self.frame.size.width;
    else
        width = self.frame.size.width - (i* self.frame.size.width - totalWidth);
    CGContextMoveToPoint(ctx, 0, lineHeight*i-1);
    CGContextAddLineToPoint(ctx, width, lineHeight*i-1);
}

CGContextStrokePath(ctx);

[super drawRect:rect]; 
}

0

Comme kovpas l'a montré, vous pouvez utiliser le cadre de sélection dans la plupart des cas, bien qu'il ne soit pas toujours garanti que le cadre de sélection s'adapte parfaitement au texte. Une boîte avec une hauteur de 50 et une taille de police de 12 peut ne pas donner les résultats souhaités selon la configuration d'UILabel.

Interrogez l'UIString dans UILabel pour déterminer ses métriques exactes et utilisez-les pour mieux placer votre soulignement quel que soit le cadre ou le cadre englobant en utilisant le code de dessin déjà fourni par kovpas.

Vous devriez également regarder la propriété «principale» d'UIFont qui donne la distance entre les lignes de base en fonction d'une police particulière. La ligne de base est l'endroit où vous souhaitez que votre soulignement soit dessiné.

Recherchez les ajouts UIKit à NSString:

(CGSize)sizeWithFont:(UIFont *)font 
//Returns the size of the string if it were to be rendered with the specified font on a single line.

(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size 
// Returns the size of the string if it were rendered and constrained to the specified size.

(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
//Returns the size of the string if it were rendered with the specified constraints.

Kenny, il semble que je puisse utiliser les 3 méthodes pour obtenir facilement la largeur de la 1ère ligne de texte, mais qu'en est-il de la 2ème 3ème et des autres lignes? Pouvez-vous donner un exemple?
semix

Je dois concéder. Il est maintenant possible d'utiliser NSString pour réaliser ce que vous voulez, à moins que quelqu'un d'autre n'ait plus à offrir. Je vais devoir suggérer comme les autres avant moi d'utiliser UIWebView et de mettre votre texte dans la vue: [webView loadHTMLString: @ "<html> <u> Texte souligné. </u> </html>" baseURL: nil ]; Laissez-le faire la mise en page et la détermination de l'endroit où les lignes doivent aller. S'il s'agit de vous voulez que la nième ligne soit soulignée et que vous ne pouvez pas savoir quelle est la nième ligne, c'est une autre question.
gnasher

0

J'utilise une vue de ligne open source et je viens de l'ajouter aux sous-vues de bouton:

 UILabel *label = termsButton.titleLabel;
 CGRect frame = label.frame;
 frame.origin.y += frame.size.height - 1;
 frame.size.height = 1;
 SSLineView *line = [[SSLineView alloc] initWithFrame:frame];
 line.lineColor = [UIColor lightGrayColor];
 [termsButton addSubview:line];

Cela a été inspiré par Karim ci-dessus.


Vous pouvez simplement utiliser UIVIew. UIView * line = [[UIView alloc] initWithFrame: frame]; line.backgroundColor = [UIColor lightGrayColor];
dzeikei

0

Basé sur les réponses de Kovpas et Damien Praca, voici une implémentation de UILabelUnderligned qui supporte également textAlignemnt .

#import <UIKit/UIKit.h>

@interface UILabelUnderlined : UILabel

@end

et la mise en œuvre:

#import "UILabelUnderlined.h"

@implementation DKUILabel

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)drawRect:(CGRect)rect {

    CGContextRef ctx = UIGraphicsGetCurrentContext();
    const CGFloat* colors = CGColorGetComponents(self.textColor.CGColor);

    CGContextSetRGBStrokeColor(ctx, colors[0], colors[1], colors[2], 1.0); // RGBA

    CGContextSetLineWidth(ctx, 1.0f);

    CGSize textSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(200, 9999)];

    // handle textAlignement

    int alignementXOffset = 0;

    switch (self.textAlignment) {
        case UITextAlignmentLeft:
            break;
        case UITextAlignmentCenter:
            alignementXOffset = (self.frame.size.width - textSize.width)/2;
            break;
        case UITextAlignmentRight:
            alignementXOffset = self.frame.size.width - textSize.width;
            break;
    }

    CGContextMoveToPoint(ctx, alignementXOffset, self.bounds.size.height - 1);
    CGContextAddLineToPoint(ctx, alignementXOffset+textSize.width, self.bounds.size.height - 1);

    CGContextStrokePath(ctx);

    [super drawRect:rect];  
}


@end

Mise à jour pour iOS 6 pour switch: switch (self.textAlignment) {case NSTextAlignmentLeft: case NSTextAlignmentJustified: case NSTextAlignmentNatural: break; case NSTextAlignmentCenter: alignementXOffset = (self.titleLabel.frame.size.width - textSize.width) / 2; Pause; case NSTextAlignmentRight: alignementXOffset = self.titleLabel.frame.size.width - textSize.width; Pause; }
pfrank

0

Voici une autre solution plus simple (la largeur du soulignement n'est pas la plus précise mais c'était assez bon pour moi)

J'ai un UIView (_view_underline)qui a un fond blanc, une hauteur de 1 pixel et je mets à jour sa largeur à chaque fois que je mets à jour le texte

// It's a shame you have to do custom stuff to underline text
- (void) underline  {
    float width = [[_txt_title text] length] * 10.0f;
    CGRect prev_frame = [_view_underline frame];
    prev_frame.size.width = width;
    [_view_underline setFrame:prev_frame];
}

0

NSUnderlineStyleAttributeName qui prend un NSNumber (où 0 n'est pas souligné) peut être ajouté à un dictionnaire d'attributs. Je ne sais pas si c'est plus facile. Mais c'était plus facile pour mes besoins.

    NSDictionary *attributes; 
    attributes = @{NSFontAttributeName:font,   NSParagraphStyleAttributeName: style, NSUnderlineStyleAttributeName:[NSNumber numberWithInteger:1]};

    [text drawInRect:CGRectMake(self.contentRect.origin.x, currentY, maximumSize.width, textRect.size.height) withAttributes:attributes];

0

Swift 4.1 ver:

 let underlineAttriString = NSAttributedString(string:"attriString", attributes:
    [NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue])

label.attributedText = underlineAttriString

0

Vous pouvez utiliser cette étiquette personnalisée! Vous pouvez également utiliser le générateur d'interface pour définir

import UIKit


class  YHYAttributedLabel : UILabel{
    
    
    @IBInspectable
    var underlineText : String = ""{
        
        didSet{

            self.attributedText = NSAttributedString(string: underlineText,
            attributes: [NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue])
        }
        
        
    }

}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.