Подчеркивая текст в UIButton

143

Кто-нибудь может подсказать, как подчеркнуть название UIButton? У меня есть пользовательский UIButton, и я хочу, чтобы заголовок был подчеркнут, но Интерфейсный Разработчик не предоставляет никакой возможности сделать это.

В Интерфейсном Разработчике, когда вы выбираете опцию «Шрифт» для кнопки, он предоставляет возможность выбрать «Нет», «Одиночный», «Двойной», «Цвет», но ни один из них не обеспечивает никаких изменений в заголовке на кнопке.

Любая помощь приветствуется.

РВН
источник
1
Вы можете использовать UITextView с приписанной строкой, добавив ссылку на нее, как в этом вопросе stackoverflow.com/questions/21629784/…
Халед Аннажар

Ответы:

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
Ник H247
источник
1
может быть, не так своевременно, как вам нужно!
Ник H247
4
Спасибо, я назвал его следующим образом: UIButton * btn = [UIUnderlinedButton buttonWithType: UIButtonTypeCustom];
hb922
2
Код работает хорошо, но я заметил, что подчеркивание не уменьшалось / увеличивалось, когда представление изменяет размер при вращении, вызванное тем, что оно drawRectне вызывается при вращении. Это может быть решено путем установки перерисовки вашей кнопки следующим образом: myButton.contentMode = UIViewContentModeRedraw;что заставляет кнопку перерисовывать при изменении границ.
AndroidNoob
4
Вы также можете переопределить setTitleметод следующим образом:objective-c - (void)setTitle:(NSString *)title forState:(UIControlState)state { [super setTitle:title forState:state]; [self setNeedsDisplay]; }
Kirualex
379

Чтобы использовать конструктор интерфейса для подчеркивания, необходимо:

  • Измените это на приписанное
  • Выделите текст в инспекторе атрибутов
  • Щелкните правой кнопкой мыши, выберите шрифт, а затем подчеркните

Подчеркните, используя IB

Видео кто-то другой сделал https://www.youtube.com/watch?v=5-ZnV3jQd9I

finneycanhelp
источник
2
Хороший вопрос @ new2ios Возможно, кто-то еще знает
finneycanhelp
1
Я задам новый вопрос, @finneycanhelp. Я надеюсь, что в Xcode 6.3 будет более простой способ. Я имею в виду, что, может быть, я могу установить ваше решение, а затем использовать setTitleс приписанным текстом. Для меня создание пользовательской кнопки для рисования подчеркивания немного экзотично (может быть, я все еще новичок в iOS, даже имея одно законченное приложение).
new2ios
2
finneydonehelped! Спасибо за это! не мог понять, почему всплывающее окно «Шрифты» не имело эффекта. Правый клик идеально подходит.
IMFletcher
2
Хороший ответ для пользователей Interface Builder для такого рода простых вещей, которые немного работают при работе с кодом. Спасибо! (Y)
Рэндика Вишман
1
Почему разработчики iOS предпочитают писать очень длинный код только для очень простой проблемы?
н 5
129

Начиная с iOS6, теперь можно использовать NSAttributedString для выполнения подчеркивания (и любой другой поддержки атрибутных строк) гораздо более гибким способом:

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

Примечание: добавил это как еще один ответ - как совершенно другое решение по сравнению с моим предыдущим.

Изменить: как ни странно (по крайней мере в iOS8), вы должны подчеркнуть первый символ, иначе он не работает!

В качестве обходного пути установите первый символ, подчеркнутый чистым цветом!

    // 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];
Ник H247
источник
9
Просто имейте в виду, что когда вы делаете это таким образом, вы также должны добавить атрибут для цвета, так как приписанный текст заголовка не будет использовать цвет, установленный вами с помощью setTitleColor: forState:
daveMac
2
Круто, и спасибо @daveMac за поддержку цвета. Для тех, кто не знает атрибута это: NSForegroundColorAttributeName
Ryan Crews
в этих методах кнопка подчеркивания близка к тексту любой метод, чтобы изменить y позицию подчеркивания?
Ilesh P
49

Вы можете сделать это в самом конструкторе интерфейсов.

  1. Выберите инспектор атрибутов
  2. Измените тип заголовка с простого на приписанный

введите описание изображения здесь

  1. Установите соответствующий размер шрифта и выравнивание текста

введите описание изображения здесь

  1. Затем выберите текст заголовка и установите шрифт как подчеркнутый

введите описание изображения здесь

Линееш К Мохан
источник
28

Это очень просто с приписанной строкой

Создает словарь с заданными атрибутами и применяется к приписанной строке. Затем вы можете установить атрибутивную строку как attibutedtitle в uibutton или attributext в 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];
Rinku
источник
Что commentString; ты скопировал из ответа @ NickH247?
значение имеет значение
21

Вот моя функция, работает в 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)
}

ОБНОВЛЕНИЕ Расширение 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

Ответ Ника - отличный, быстрый способ сделать это.

Я добавил поддержку drawRectтеней.

Ответ Ника не учитывает, если под заголовком вашей кнопки есть тень под текстом:

введите описание изображения здесь

Но вы можете переместить подчеркивание на высоту тени следующим образом:

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

Тогда вы получите что-то вроде этого:

введите описание изображения здесь

Энни
источник
self.titleLabel.font.descender; это устарело в iOS 3.0
KING
5

Для Swift 3 может использоваться следующее расширение:

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

Расширяя ответ @Nick H247, я столкнулся с проблемой, когда сначала подчеркивание не перерисовывалось при изменении размера кнопки при повороте; это можно решить, установив для вашей кнопки перерисовку следующим образом:

myButton.contentMode = UIViewContentModeRedraw; 

Это заставляет кнопку перерисовываться при изменении границ.

Во-вторых, исходный код предполагал, что у вас в кнопке была только 1 строка текста (моя кнопка оборачивается до 2 строк при вращении), а подчеркивание появляется только в последней строке текста. Код drawRect можно изменить, чтобы сначала рассчитать количество строк в кнопке, а затем поставить подчеркивание на каждой строке, а не только на нижней части, например:

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

}


}

Надеюсь, этот код поможет кому-то еще!

AndroidNoob
источник
3

В быстром

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

Вы можете использовать этот код для добавления подчеркивания с интервалом в кнопке.

  • Когда я пытался нарисовать подчеркивание из конструктора интерфейса. Это похоже на изображение ниже.

1 - Справочник разработчика интерфейса

введите описание изображения здесь

  • И после использования приведенного ниже кода я добился результата, как я хотел.

2 - используя описанный код

введите описание изображения здесь

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)
    }
Аяз Рафаи
источник
Спасибо за этот фрагмент кода, который может предоставить некоторую ограниченную, немедленную помощь. Правильное объяснение значительно улучшило бы его долгосрочную ценность, показав, почему это хорошее решение проблемы, и сделало бы его более полезным для будущих читателей с другими, похожими вопросами. Пожалуйста, измените свой ответ, чтобы добавить некоторые объяснения, в том числе предположения, которые вы сделали.
Тоби Спейт
3

Версия Swift 5.0, которая работает с сентября 2019 года в 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)
  }
}

Чтобы использовать его, сначала установите название кнопки, button.setTitle("Button Title", for: .normal)а затем вызовите button.underlineText()ее, чтобы подчеркнуть этот заголовок.

Макс Десятов
источник
1
Я могу подтвердить, что это работает на старых версиях iOS 10.3.1, Xcode 10.3 не поддерживает симуляторы старше, чем на Mojave, насколько я знаю.
Макс Десятов
2

Как справится со случаем, когда мы будем держать нажатой подчеркнутую кнопку? В этом случае цвет текста кнопки изменяется в соответствии с выделенным цветом, но линия остается оригинального цвета. Допустим, если цвет текста кнопки в обычном состоянии черный, то ее подчеркивание также будет иметь черный цвет. Цвет подсветки кнопки - белый. Удерживая кнопку нажатой, цвет текста кнопки изменяется с черного на белый, но цвет подчеркивания остается черным.

Парвез Куреши
источник
2
Вы можете проверить, выделена ли кнопка и / или выбрана, и установить цвет соответствующим образом. не уверен, будет ли запрашиваться перерисовка автоматически, в противном случае вам потребуется переопределить setSelected / setHighlighted и вызвать super и [self setNeedsDisplay]
Ник H247,
2

Я считаю, что это какая-то ошибка в редакторе шрифтов в XCode. Если вы используете конструктор интерфейса, вам нужно изменить заголовок с Обычный на Атрибут, откройте TextEdit, создайте подчеркнутый текст и скопируйте и вставьте его в текстовое поле в XCode

dangh
источник
2

Ник Н247 ответ, но Свифт подход:

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);
    }
}
el.severo
источник
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)
    }
LuAndre
источник
1

Версия Swift 3 для ответа @ NickH247 с пользовательским цветом подчеркивания, шириной линии и пропуском :

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")
    }
}
bughana
источник