Text in UIButton unterstreichen


143

Kann jemand vorschlagen, wie man den Titel eines UIButton unterstreicht? Ich habe einen UIButton vom Typ Benutzerdefiniert und möchte, dass der Titel unterstrichen wird, aber der Interface Builder bietet keine Option dazu.

Wenn Sie im Interface Builder die Schriftartoption für eine Schaltfläche auswählen, können Sie die Option Keine, Einzel, Doppel, Farbe auswählen. Bei keiner dieser Optionen werden jedoch Änderungen am Titel auf der Schaltfläche vorgenommen.

Jede Hilfe geschätzt.


1
Sie können UITextView mit einer zugewiesenen Zeichenfolge verwenden und einen Link hinzufügen, wie in dieser Frage. Stackoverflow.com/questions/21629784/…
Khaled Annajar

Antworten:


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
vielleicht nicht so aktuell wie nötig!
Nick H247

4
Danke, am Ende habe ich es wie folgt aufgerufen: UIButton * btn = [UIUnderlinedButton buttonWithType: UIButtonTypeCustom];
hb922

2
Der Code funktioniert gut, aber ich habe festgestellt, dass die Unterstreichung nicht verkleinert / vergrößert wurde, wenn sich die Größe der Ansicht beim Drehen ändert, drawRectweil sie beim Drehen nicht aufgerufen wurde. Dies kann gelöst werden, indem Sie Ihre Schaltfläche so einstellen, dass sie neu gezeichnet wird. Dadurch wird die Schaltfläche gezwungen myButton.contentMode = UIViewContentModeRedraw;, neu zu zeichnen, wenn sich die Grenzen ändern.
AndroidNoob

4
Sie können die setTitleMethode auch folgendermaßen überschreiben :objective-c - (void)setTitle:(NSString *)title forState:(UIControlState)state { [super setTitle:title forState:state]; [self setNeedsDisplay]; }
Kirualex

378

Um den Interface Builder zum Unterstreichen zu verwenden, muss man:

  • Ändern Sie es in zugeschrieben
  • Markieren Sie den Text im Attributinspektor
  • Klicken Sie mit der rechten Maustaste, wählen Sie Schriftart und dann Unterstreichen

Unterstreichen Sie mit IB

Video, das jemand anderes gemacht hat https://www.youtube.com/watch?v=5-ZnV3jQd9I


2
Gute Frage @ new2ios Vielleicht weiß jemand anderes
finneycanhelp

1
Ich werde eine neue Frage stellen, @finneycanhelp. Ich hoffe, dass es in Xcode 6.3 einen einfacheren Weg gibt. Ich meine, vielleicht kann ich Ihre Lösung festlegen und sie dann setTitlemit zugeordnetem Text verwenden. Für mich ist das Erstellen einer benutzerdefinierten Schaltfläche zum Unterstreichen etwas exotisch (möglicherweise bin ich noch neu in iOS, selbst wenn eine App abgeschlossen ist).
New2ios

2
finneydonehelped! Danke dafür! Ich konnte nicht herausfinden, warum das Popup-Dialogfeld "Schriftarten" keine Auswirkung hatte. Der Rechtsklick ist perfekt.
IMFletcher

2
Gute Antwort für Benutzer von Interface Builder für diese Art von einfachen Dingen, die in Code ein wenig Arbeit bedeuten. Vielen Dank! (Y)
Randika Vishman

1
Warum schreiben iOS-Entwickler einen sehr langen Code nur für ein sehr einfaches Problem?
mr5

129

Ab iOS6 ist es jetzt möglich, einen NSAttributedString zu verwenden, um das Unterstreichen (und alles andere, was Zeichenfolgen unterstützen) auf viel flexiblere Weise auszuführen:

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];

Hinweis: Dies wurde als weitere Antwort hinzugefügt - da es eine völlig andere Lösung als meine vorherige ist.

Bearbeiten: Seltsamerweise (zumindest in iOS8) müssen Sie das erste Zeichen unterstreichen, sonst funktioniert es nicht!

Um dieses Problem zu umgehen, setzen Sie das erste Zeichen mit klarer Farbe unterstrichen!

    // 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
Beachten Sie jedoch, dass Sie auf diese Weise auch ein Attribut für die Farbe hinzufügen müssen, da der zugewiesene Titeltext nicht die Farbe verwendet, die Sie mit setTitleColor: forState festgelegt haben:
daveMac

2
Super, und danke @daveMac für die Hinweise auf die Farbe. Für diejenigen, die das Attribut nicht kennen, ist es: NSForegroundColorAttributeName
Ryan Crews

Bei diesen Methoden befindet sich die Unterstreichung der Schaltfläche in der Nähe von Text. Gibt es eine Methode zum Ändern der y-Position der Unterstreichung?
Ilesh P

49

Sie können dies im Interface Builder selbst tun.

  1. Wählen Sie den Attributinspektor aus
  2. Ändern Sie den Titeltyp von "einfach" in "zugeordnet"

Geben Sie hier die Bildbeschreibung ein

  1. Stellen Sie die entsprechende Schriftgröße und Textausrichtung ein

Geben Sie hier die Bildbeschreibung ein

  1. Wählen Sie dann den Titeltext aus und stellen Sie die Schriftart als unterstrichen ein

Geben Sie hier die Bildbeschreibung ein


28

Es ist sehr einfach mit zugeschriebener Zeichenfolge

Erstellt ein Wörterbuch mit festgelegten Attributen und wird auf die zugewiesene Zeichenfolge angewendet. Anschließend können Sie die zugewiesene Zeichenfolge in uibutton als attibutedtitle oder in uilabel als attributedtext festlegen.

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];

Was ist commentString; Hast du aus der Antwort von @ NickH247 kopiert?
Bedeutung-Angelegenheiten

21

Hier ist meine Funktion, funktioniert 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)
}

UPDATE Swift 3.0 Erweiterung:

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

Nicks Antwort ist eine großartige und schnelle Möglichkeit, dies zu tun.

Ich habe Unterstützung drawRectfür Schatten hinzugefügt .

Nicks Antwort berücksichtigt nicht, ob Ihr Schaltflächentitel einen Schatten unter dem Text hat:

Geben Sie hier die Bildbeschreibung ein

Sie können die Unterstreichung jedoch wie folgt um die Höhe des Schattens nach unten verschieben:

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

Dann bekommst du so etwas:

Geben Sie hier die Bildbeschreibung ein


self.titleLabel.font.descender; Dies wurde in iOS 3.0
KING

5

Für Swift 3 kann die folgende Erweiterung verwendet werden:

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

Bei der Erweiterung der Antwort von @Nick H247 trat ein Problem auf, bei dem zunächst die Unterstreichung nicht neu gezeichnet wurde, wenn die Größe der Schaltfläche beim Drehen geändert wurde. Dies kann gelöst werden, indem Sie Ihre Schaltfläche so einstellen, dass sie wie folgt neu gezeichnet wird:

myButton.contentMode = UIViewContentModeRedraw; 

Dies zwingt die Schaltfläche zum Neuzeichnen, wenn sich die Grenzen ändern.

Zweitens ging der ursprüngliche Code davon aus, dass Sie nur 1 Textzeile in der Schaltfläche hatten (meine Schaltfläche wird beim Drehen in 2 Zeilen umgebrochen), und die Unterstreichung wird nur in der letzten Textzeile angezeigt. Der drawRect-Code kann geändert werden, um zuerst die Anzahl der Zeilen in der Schaltfläche zu berechnen und dann jede Zeile und nicht nur die untere Zeile zu unterstreichen:

 - (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);

}


}

Hoffe, dieser Code hilft jemand anderem!


3

In kurzer Zeit

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

Mit diesem Code können Sie eine Unterstreichung mit einem Abstand in der Schaltfläche hinzufügen.

  • Als ich versuchte, eine Unterstreichung aus dem Interface Builder zu zeichnen. Es sieht aus wie unter Bild.

1 - Referenz zum Interface Builder

Geben Sie hier die Bildbeschreibung ein

  • Und nachdem ich den folgenden Code verwendet hatte, erreichte ich das gewünschte Ergebnis.

2 - unter Verwendung des beschriebenen Codes

Geben Sie hier die Bildbeschreibung ein

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)
    }

Vielen Dank für dieses Code-Snippet, das möglicherweise nur begrenzte, sofortige Hilfe bietet. Eine richtige Erklärung würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist, und es für zukünftige Leser mit anderen, ähnlichen Fragen nützlicher machen. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der von Ihnen getroffenen Annahmen.
Toby Speight

3

Die Swift 5.0-Version, die ab September 2019 in Xcode 10.3 funktioniert:

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)
  }
}

Um es zu verwenden, setzen Sie zuerst Ihren Schaltflächentitel mit button.setTitle("Button Title", for: .normal)und rufen Sie dann button.underlineText()auf, um diesen Titel zu unterstreichen.


1
Ich kann bestätigen, dass dies bei Versionen funktioniert, die so alt sind wie iOS 10.3.1. Xcode 10.3 unterstützt meines Wissens keine Simulatoren, die älter als Mojave sind.
Max Desiatov

2

Wie wird man mit dem Fall umgehen, wenn wir einen unterstrichenen Knopf gedrückt halten? In diesem Fall ändert sich die Textfarbe der Schaltfläche entsprechend der hervorgehobenen Farbe, aber die Linie bleibt in der Originalfarbe. Angenommen, wenn die Textfarbe der Schaltfläche im Normalzustand schwarz ist, hat ihre Unterstreichung auch eine schwarze Farbe. Die hervorgehobene Farbe der Schaltfläche ist weiß. Wenn Sie die Taste gedrückt halten, wird die Textfarbe der Schaltfläche von Schwarz in Weiß geändert, die Unterstreichungsfarbe bleibt jedoch schwarz.


2
Sie können testen, ob die Schaltfläche markiert und / oder ausgewählt ist, und die Farbe entsprechend einstellen. Ich bin mir nicht sicher, ob das Neuzeichnen automatisch angefordert wird. Wenn nicht, müssten Sie setSelected / setHighlighted überschreiben und super und [self setNeedsDisplay] aufrufen
Nick H247

2

Ich glaube, es ist ein Fehler im Schrifteditor in XCode. Wenn Sie den Interface Builder verwenden, müssen Sie den Titel von "Einfach" in "Attributiert" ändern, TextEdit öffnen, unterstrichenen Text erstellen und in das Textfeld in XCode kopieren und einfügen


2

Antwort von Nick H247, aber schneller Ansatz:

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

Swift 3-Version für die Antwort von @ NickH247 mit benutzerdefinierter Unterstreichungsfarbe, Linienbreite und Lücke:

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")
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.