Sottolineatura del testo in UIButton


143

Qualcuno può suggerire come sottolineare il titolo di un UIButton? Ho un UIButton di tipo personalizzato e voglio che il titolo sia sottolineato, ma Interface Builder non fornisce alcuna opzione per farlo.

In Interface Builder quando si seleziona l'opzione Carattere per un pulsante, fornisce l'opzione per selezionare Nessuno, Singolo, Doppio, Colore, ma nessuna di queste fornisce modifiche al titolo sul pulsante.

Qualsiasi aiuto apprezzato.


1
È possibile utilizzare UITextView con lo spago attribuito l'aggiunta di un link ad esso come in questa domanda stackoverflow.com/questions/21629784/...
Khaled Annajar

Risposte:


79

UIUnderlinedButton.h

@interface UIUnderlinedButton : UIButton {

}


+ (UIUnderlinedButton*) underlinedButton;
@end

UIUnderlinedButton.m

@implementation UIUnderlinedButton

+ (UIUnderlinedButton*) underlinedButton {
    UIUnderlinedButton* button = [[UIUnderlinedButton alloc] init];
    return [button autorelease];
}

- (void) drawRect:(CGRect)rect {
    CGRect textRect = self.titleLabel.frame;

    // need to put the line at top of descenders (negative value)
    CGFloat descender = self.titleLabel.font.descender;

    CGContextRef contextRef = UIGraphicsGetCurrentContext();

    // set to same colour as text
    CGContextSetStrokeColorWithColor(contextRef, self.titleLabel.textColor.CGColor);

    CGContextMoveToPoint(contextRef, textRect.origin.x, textRect.origin.y + textRect.size.height + descender);

    CGContextAddLineToPoint(contextRef, textRect.origin.x + textRect.size.width, textRect.origin.y + textRect.size.height + descender);

    CGContextClosePath(contextRef);

    CGContextDrawPath(contextRef, kCGPathStroke);
}


@end

1
forse non tempestivo quanto ti serviva!
Nick H247,

4
Grazie, ho finito per chiamarlo come segue: UIButton * btn = [UIUnderlinedButton buttonWithType: UIButtonTypeCustom];
hb922,

2
Il codice funziona bene, ma ho notato che la sottolineatura non si riduce / aumenta quando la vista cambia dimensione durante la rotazione, a causa della drawRectmancata chiamata alla rotazione. Questo può essere risolto impostando il pulsante per ridisegnare in questo modo: myButton.contentMode = UIViewContentModeRedraw;ciò forza il pulsante a ridisegnare quando i limiti cambiano.
AndroidNoob,

4
Puoi anche ignorare il setTitlemetodo in questo modo:objective-c - (void)setTitle:(NSString *)title forState:(UIControlState)state { [super setTitle:title forState:state]; [self setNeedsDisplay]; }
Kirualex

379

Per sottolineare l'interfaccia builder, è necessario:

  • Modificalo in attribuito
  • Evidenzia il testo nella finestra di ispezione Attributi
  • Fare clic con il tasto destro, selezionare Carattere e poi Sottolineato

Sottolineare utilizzando IB

Video realizzato da qualcun altro https://www.youtube.com/watch?v=5-ZnV3jQd9I


2
Buona domanda @ new2ios Forse qualcun altro lo sa
finneycanhelp il

1
Farò una nuova domanda, @finneycanhelp. Spero che in Xcode 6.3 ci sia un modo più semplice. Voglio dire, posso essere in grado di impostare la soluzione e quindi utilizzare setTitlecon il testo attribuito. Per me la creazione di un pulsante personalizzato da sottolineare è un po 'esotica (forse sono ancora nuovo su iOS anche con un'app completata).
new2ios

2
finneydonehelped! grazie per questo! non riuscivo a capire perché la finestra di dialogo popup Caratteri non avesse alcun effetto. Il clic destro è perfetto.
IMFletcher

2
Buona risposta per gli utenti di Interface Builder per questo tipo di cose semplici che sono un po 'di lavoro quando si fa in Code. Grazie! (Y)
Randika Vishman,

1
Perché gli sviluppatori iOS preferiscono scrivere un codice molto lungo solo per un problema molto semplice?
mr5

129

Da iOS6 è ora possibile utilizzare un NSAttributedString per eseguire la sottolineatura (e qualsiasi altro supporto per le stringhe attribuito) in un modo molto più flessibile:

NSMutableAttributedString *commentString = [[NSMutableAttributedString alloc] initWithString:@"The Quick Brown Fox"];

[commentString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0, [commentString length])];

[button setAttributedTitle:commentString forState:UIControlStateNormal];

Nota: aggiunta questa come un'altra risposta, in quanto è una soluzione totalmente diversa dalla mia precedente.

Modifica: stranamente (almeno in iOS8) devi sottolineare il primo personaggio altrimenti non funziona!

quindi, come soluzione alternativa, imposta il primo carattere sottolineato con un colore chiaro!

    // underline Terms and condidtions
    NSMutableAttributedString* tncString = [[NSMutableAttributedString alloc] initWithString:@"View Terms and Conditions"];

    // workaround for bug in UIButton - first char needs to be underlined for some reason!
    [tncString addAttribute:NSUnderlineStyleAttributeName
                      value:@(NSUnderlineStyleSingle)
                      range:(NSRange){0,1}];
    [tncString addAttribute:NSUnderlineColorAttributeName value:[UIColor clearColor] range:NSMakeRange(0, 1)];


    [tncString addAttribute:NSUnderlineStyleAttributeName
                      value:@(NSUnderlineStyleSingle)
                      range:(NSRange){5,[tncString length] - 5}];

    [tncBtn setAttributedTitle:tncString forState:UIControlStateNormal];

9
Tieni presente che quando lo fai in questo modo, devi anche aggiungere l'attributo per il colore, poiché il testo del titolo attribuito non utilizzerà il colore impostato utilizzando setTitleColor: forState:
daveMac

2
Fantastico, e grazie @daveMac per l'heads up sul colore. Per coloro che non conoscono l'attributo è: NSForegroundColorAttributeName
Ryan Crews

in questi metodi il pulsante sottolineato è vicino al testo qualsiasi metodo per modificare la posizione y della sottolineatura?
Ilesh P

49

Puoi farlo nel generatore di interfaccia stesso.

  1. Seleziona la finestra di ispezione degli attributi
  2. Cambia il tipo di titolo da semplice ad attribuito

inserisci qui la descrizione dell'immagine

  1. Imposta la dimensione del carattere e l'allineamento del testo appropriati

inserisci qui la descrizione dell'immagine

  1. Quindi selezionare il testo del titolo e impostare il carattere come sottolineato

inserisci qui la descrizione dell'immagine


28

È molto semplice con una stringa attribuita

Crea un dizionario con attributi impostati e si applica alla stringa attribuita. Quindi puoi impostare la stringa attribuita come attibutedtitle in uibutton o attribuito in uilabel.

NSDictionary *attrDict = @{NSFontAttributeName : [UIFont
 systemFontOfSize:14.0],NSForegroundColorAttributeName : [UIColor
 whiteColor]};
 NSMutableAttributedString *title =[[NSMutableAttributedString alloc] initWithString:@"mybutton" attributes: attrDict]; 
[title addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0,[commentString length])]; [btnRegLater setAttributedTitle:title forState:UIControlStateNormal];

Cosa c'è commentString; hai copiato dalla risposta di @ NickH247?
significato-argomenti

21

Ecco la mia funzione, funziona in Swift 1.2.

func underlineButton(button : UIButton, text: String) {

    var titleString : NSMutableAttributedString = NSMutableAttributedString(string: text)
    titleString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSMakeRange(0, count(text.utf8)))
    button.setAttributedTitle(titleString, forState: .Normal)
}

Estensione UPDATE Swift 3.0:

extension UIButton {
    func underlineButton(text: String) {
        let titleString = NSMutableAttributedString(string: text)
        titleString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: NSMakeRange(0, text.characters.count))
        self.setAttributedTitle(titleString, for: .normal)
    }
}

13

La risposta di Nick è un ottimo modo rapido per farlo.

Ho aggiunto il supporto drawRectper le ombre.

La risposta di Nick non tiene conto se il titolo del tuo pulsante ha un'ombra sotto il testo:

inserisci qui la descrizione dell'immagine

Ma puoi spostare la sottolineatura verso il basso dell'altezza dell'ombra in questo modo:

CGFloat descender = self.titleLabel.font.descender;
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGFloat shadowHeight = self.titleLabel.shadowOffset.height;
descender += shadowHeight;

Quindi otterrai qualcosa del genere:

inserisci qui la descrizione dell'immagine


self.titleLabel.font.descender; questo è stato deprezzato in iOS 3.0
KING il

5

Per Swift 3 è possibile utilizzare la seguente estensione:

extension UIButton {
    func underlineButton(text: String) {
        let titleString = NSMutableAttributedString(string: text)
        titleString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.styleSingle.rawValue, range: NSMakeRange(0, text.characters.count))
        self.setAttributedTitle(titleString, for: .normal)
    }
}

4
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
CGRect textRect = self.titleLabel.frame;

// need to put the line at top of descenders (negative value)
CGFloat descender = self.titleLabel.font.descender;

CGContextRef contextRef = UIGraphicsGetCurrentContext();
UIColor *colr;
// set to same colour as text
if (self.isHighlighted || self.isSelected) {
    colr=self.titleLabel.highlightedTextColor;
}
else{
    colr= self.titleLabel.textColor;
}
CGContextSetStrokeColorWithColor(contextRef, colr.CGColor);

CGContextMoveToPoint(contextRef, textRect.origin.x, textRect.origin.y +        textRect.size.height + descender);

CGContextAddLineToPoint(contextRef, textRect.origin.x + textRect.size.width, textRect.origin.y + textRect.size.height + descender);

CGContextClosePath(contextRef);

CGContextDrawPath(contextRef, kCGPathStroke);
}
//Override this to change the underline color to highlighted color
-(void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
// [self setNeedsDisplay];
}

3

Espandendo la risposta di @Nick H247, ho riscontrato un problema in cui la sottolineatura non veniva ridisegnata quando il pulsante veniva ridimensionato in rotazione; questo può essere risolto impostando il pulsante per ridisegnare in questo modo:

myButton.contentMode = UIViewContentModeRedraw; 

Ciò forza il pulsante a ridisegnare quando i limiti cambiano.

In secondo luogo, il codice originale presupponeva che nel pulsante fosse presente solo 1 riga di testo (il mio pulsante si sposta su 2 righe in rotazione) e la sottolineatura appare solo sull'ultima riga di testo. Il codice drawRect può essere modificato per calcolare prima il numero di righe nel pulsante, quindi inserire una sottolineatura su ogni riga anziché solo in fondo, in questo modo:

 - (void) drawRect:(CGRect)rect {
CGRect textRect = self.titleLabel.frame;

// need to put the line at top of descenders (negative value)
CGFloat descender = self.titleLabel.font.descender;

CGContextRef contextRef = UIGraphicsGetCurrentContext();

// set to same colour as text
CGContextSetStrokeColorWithColor(contextRef, self.titleLabel.textColor.CGColor);

CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font
                            constrainedToSize:self.titleLabel.frame.size
                                lineBreakMode:UILineBreakModeWordWrap];

CGSize labelSizeNoWrap = [self.titleLabel.text sizeWithFont:self.titleLabel.font forWidth:self.titleLabel.frame.size.width lineBreakMode:UILineBreakModeMiddleTruncation ];

int numberOfLines = abs(labelSize.height/labelSizeNoWrap.height);

for(int i = 1; i<=numberOfLines;i++) {
 //        Original code
 //        CGContextMoveToPoint(contextRef, textRect.origin.x, textRect.origin.y + textRect.size.height + descender + PADDING);
 //        
 //        CGContextAddLineToPoint(contextRef, textRect.origin.x + textRect.size.width, textRect.origin.y + textRect.size.height + descender);

    CGContextMoveToPoint(contextRef, textRect.origin.x, textRect.origin.y + (labelSizeNoWrap.height*i) + descender + PADDING);

    CGContextAddLineToPoint(contextRef, textRect.origin.x + textRect.size.width, textRect.origin.y + (labelSizeNoWrap.height*i) + descender);

    CGContextClosePath(contextRef);

    CGContextDrawPath(contextRef, kCGPathStroke);

}


}

Spero che questo codice aiuti qualcun altro!


3

In fretta

func underlineButton(button : UIButton) {

var titleString : NSMutableAttributedString = NSMutableAttributedString(string: button.titleLabel!.text!)
titleString.addAttribute(NSUnderlineStyleAttributeName, value: NSUnderlineStyle.StyleSingle.rawValue, range: NSMakeRange(0, button.titleLabel!.text!.utf16Count))
button.setAttributedTitle(titleString, forState: .Normal)}

3

È possibile utilizzare questo codice per aggiungere la sottolineatura con il pulsante spaziatura in.

  • Quando ho provato a disegnare una sottolineatura dal generatore di interfacce. Sembra sotto l'immagine.

1 - Riferimento del builder di interfaccia

inserisci qui la descrizione dell'immagine

  • E dopo aver usato il codice qui sotto ho ottenuto il risultato come volevo.

2 - utilizzando il codice descritto

inserisci qui la descrizione dell'immagine

public func setTextUnderline()
    {
        let dummyButton: UIButton = UIButton.init()
        dummyButton.setTitle(self.titleLabel?.text, for: .normal)
        dummyButton.titleLabel?.font = self.titleLabel?.font
        dummyButton.sizeToFit()

        let dummyHeight = dummyButton.frame.size.height + 3

        let bottomLine = CALayer()
        bottomLine.frame = CGRect.init(x: (self.frame.size.width - dummyButton.frame.size.width)/2, y: -(self.frame.size.height - dummyHeight), width: dummyButton.frame.size.width, height: 1.0)
        bottomLine.backgroundColor = self.titleLabel?.textColor.cgColor
        self.layer.addSublayer(bottomLine)
    }

Grazie per questo frammento di codice, che potrebbe fornire un aiuto limitato e immediato. Una spiegazione adeguata migliorerebbe notevolmente il suo valore a lungo termine mostrando perché questa è una buona soluzione al problema e lo renderebbe più utile ai futuri lettori con altre domande simili. Si prega di modificare la risposta di aggiungere qualche spiegazione, tra le ipotesi che hai fatto.
Toby Speight,

3

La versione Swift 5.0 che funziona a settembre 2019 in Xcode 10.3:

extension UIButton {
  func underlineText() {
    guard let title = title(for: .normal) else { return }

    let titleString = NSMutableAttributedString(string: title)
    titleString.addAttribute(
      .underlineStyle,
      value: NSUnderlineStyle.single.rawValue,
      range: NSRange(location: 0, length: title.count)
    )
    setAttributedTitle(titleString, for: .normal)
  }
}

Per usarlo, imposta prima il titolo del tuo pulsante button.setTitle("Button Title", for: .normal)e poi chiama button.underlineText()per sottolineare quel titolo.


1
Posso confermare che funziona su versioni precedenti a iOS 10.3.1, Xcode 10.3 non supporta simulatori più vecchi di quelli su Mojave, per quanto ne so.
Max Desiatov,

2

Come si gestirà il caso quando si tiene premuto un pulsante sottolineato? In tal caso, il colore del pulsante cambia in base al colore evidenziato, ma la linea rimane del colore originale. Supponiamo che se il colore del testo del pulsante nello stato normale è nero, anche la sua sottolineatura avrà un colore nero. Il colore evidenziato del pulsante è bianco. Tenendo premuto il pulsante, il colore del testo del pulsante cambia da nero a bianco ma il colore sottolineato rimane nero.


2
È possibile verificare se il pulsante è evidenziato e o selezionato e impostare il colore di conseguenza. non sono sicuro se il ridisegno verrà richiesto automaticamente, in caso contrario sarà necessario sovrascrivere setSelected / setHighlighted e chiamare super e [self setNeedsDisplay]
Nick H247,

2

Credo che sia un bug nell'editor dei font in XCode. Se si utilizza il builder di interfaccia, è necessario modificare il titolo da Pianura a Attribuito, aprire TextEdit per creare il testo sottolineato e copiare e incollare nella casella di testo in XCode


2

La risposta di Nick H247 ma l'approccio Swift:

import UIKit

class UnderlineUIButton: UIButton {

    override func drawRect(rect: CGRect) {
        super.drawRect(rect)

        let textRect = self.titleLabel!.frame

        var descender = self.titleLabel?.font.descender

        var contextRef: CGContextRef = UIGraphicsGetCurrentContext();

        CGContextSetStrokeColorWithColor(contextRef, self.titleLabel?.textColor.CGColor);

        CGContextMoveToPoint(contextRef, textRect.origin.x, textRect.origin.y + textRect.size.height + descender!);

        CGContextAddLineToPoint(contextRef, textRect.origin.x + textRect.size.width, textRect.origin.y + textRect.size.height + descender!);

        CGContextClosePath(contextRef);

        CGContextDrawPath(contextRef, kCGPathStroke);
    }
}

2
func underline(text: String, state: UIControlState = .normal, color:UIColor? = nil) {
        var titleString = NSMutableAttributedString(string: text)

        if let color = color {
            titleString = NSMutableAttributedString(string: text,
                               attributes: [NSForegroundColorAttributeName: color])
        }

        let stringRange = NSMakeRange(0, text.characters.count)
        titleString.addAttribute(NSUnderlineStyleAttributeName,
                                 value: NSUnderlineStyle.styleSingle.rawValue,
                                 range: stringRange)

        self.setAttributedTitle(titleString, for: state)
    }

1

Versione Swift 3 per la risposta di @ NickH247 con colore di sottolineatura, larghezza di linea e spazio personalizzati:

import Foundation

class UnderlinedButton: UIButton {

    private let underlineColor: UIColor
    private let thickness: CGFloat
    private let gap: CGFloat

    init(underlineColor: UIColor, thickness: CGFloat, gap: CGFloat, frame: CGRect? = nil) {
        self.underlineColor = underlineColor
        self.thickness = thickness
        self.gap = gap
        super.init(frame: frame ?? .zero)
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)

        guard let textRect = titleLabel?.frame,
            let decender = titleLabel?.font.descender,
            let context = UIGraphicsGetCurrentContext() else { return }

        context.setStrokeColor(underlineColor.cgColor)
        context.move(to: CGPoint(x: textRect.origin.x, y: textRect.origin.y + textRect.height + decender + gap))
        context.setLineWidth(thickness)
        context.addLine(to: CGPoint(x: textRect.origin.x + textRect.width, y: textRect.origin.y + textRect.height + decender + gap))
        context.closePath()
        context.drawPath(using: .stroke)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.