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?

semix
la source
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.

kovpas
la source
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
ytll21
la source
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];
Sana
la source
+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

Paulo Ferreira
la source
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

Guntis Treulands
la source
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];
}
Karim
la source
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;
}
Jill Wong
la source
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 ;-)

Youssman
la source
7
NSMutableAttributedString *text = [self.myUILabel.attributedText mutableCopy];
[text addAttribute:NSUnderlineStyleAttributeName value:@(NSUnderlineStyleSingle) range:NSMakeRange(0, text.length)];
self.myUILabel.attributedText = text;
Romain Solodyashkin
la source
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];
}
nfinfu
la source
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;
Dhaval Dobariya
la source
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
Monsieur Javed Multani
la source
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
Damien Praca
la source
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]; 
}
Piyush
la source
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.
gnasher
la source
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.

David H
la source
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
Pascal
la source
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];
}
Ege Akpinar
la source
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];
epaus
la source
0

Swift 4.1 ver:

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

label.attributedText = underlineAttriString
Abdoelrhman
la source
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])
        }
        
        
    }

}
Ucdemir
la source