Gạch chân văn bản trong UIButton


143

Bất cứ ai cũng có thể đề nghị làm thế nào để gạch chân tiêu đề của một UIButton? Tôi có một UIButton loại Tùy chỉnh và tôi muốn Tiêu đề được gạch chân, nhưng Trình tạo giao diện không cung cấp bất kỳ tùy chọn nào để làm như vậy.

Trong Trình tạo giao diện khi bạn chọn Tùy chọn phông chữ cho Nút, nó cung cấp tùy chọn để chọn Không, Đơn, Đôi, Màu nhưng không có thay đổi nào cung cấp bất kỳ thay đổi nào cho Tiêu đề trên Nút.

Bất kỳ trợ giúp đánh giá cao.


1
Bạn có thể sử dụng UITextView với chuỗi được gán thêm một liên kết đến nó như trong câu hỏi này stackoverflow.com/questions/21629784/
mẹo

Câu trả lời:


79

UIUnderlinesButton.h

@interface UIUnderlinedButton : UIButton {

}


+ (UIUnderlinedButton*) underlinedButton;
@end

UIUnderlinesButton.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
có thể không kịp thời như bạn cần
Nick H247

4
Cảm ơn, cuối cùng tôi đã gọi nó như sau: UIButton * btn = [UIUnderlinesButton nútWithType: UIButtonTypeCustom];
hb922

2
Mã này hoạt động tốt, nhưng tôi nhận thấy phần gạch chân không co lại / phát triển khi chế độ xem thay đổi kích thước khi xoay, do drawRectkhông được gọi khi xoay. Điều này có thể được giải quyết bằng cách đặt nút của bạn để vẽ lại như vậy: myButton.contentMode = UIViewContentModeRedraw;điều này buộc nút phải vẽ lại khi giới hạn thay đổi.
AndroidNoob

4
Bạn cũng có thể ghi đè setTitlephương thức như vậy:objective-c - (void)setTitle:(NSString *)title forState:(UIControlState)state { [super setTitle:title forState:state]; [self setNeedsDisplay]; }
Kirualex

378

Để sử dụng trình xây dựng giao diện để gạch chân, người ta phải:

  • Thay đổi nó thành quy
  • Làm nổi bật văn bản trong thanh tra thuộc tính
  • Nhấp chuột phải, chọn Phông chữ và Gạch chân

Gạch chân bằng cách sử dụng IB

Video người khác thực hiện https://www.youtube.com/watch?v=5-ZnV3jQd9I


2
Câu hỏi hay @ new2ios Có lẽ ai đó biết
finneycanhelp

1
Tôi sẽ hỏi câu hỏi mới, @finneycanhelp. Tôi hy vọng rằng trong Xcode 6.3 sẽ có cách dễ dàng hơn. Tôi có nghĩa là tôi có thể thiết lập giải pháp của bạn và sau đó sử dụng setTitlevới văn bản quy kết. Đối với tôi, việc tạo nút tùy chỉnh để vẽ gạch chân là một chút kỳ lạ (có thể tôi vẫn còn mới đối với iOS ngay cả khi đã hoàn thành một Ứng dụng).
new2ios

2
Finneydonehelped! cảm ơn vì điều đó! không thể hiểu tại sao hộp thoại bật lên Phông chữ không có hiệu lực. Nhấp chuột phải là hoàn hảo.
IMFletcher

2
Câu trả lời tốt cho người dùng Interface Builder cho loại điều đơn giản này chỉ là một chút công việc khi thực hiện trong Code. Cảm ơn! (Y)
Randika Vishman 22/03/2016

1
Tại sao các nhà phát triển iOS thích viết một mã rất dài chỉ cho một vấn đề rất đơn giản?
mr5

129

Từ iOS6, giờ đây có thể sử dụng NSAttributionString để thực hiện gạch chân (và bất kỳ thứ gì khác hỗ trợ chuỗi được quy cho) theo cách linh hoạt hơn nhiều:

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

Lưu ý: đã thêm câu hỏi này như một câu trả lời khác - vì đây là một giải pháp hoàn toàn khác với câu trả lời trước của tôi.

Chỉnh sửa: kỳ lạ (ít nhất là trong iOS8) bạn phải gạch chân ký tự đầu tiên nếu không nó không hoạt động!

Vì vậy, như một cách giải quyết, đặt char đầu tiên được gạch chân với màu sắc rõ ràng!

    // 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
Chỉ cần lưu ý rằng khi bạn thực hiện theo cách này, bạn cũng phải thêm thuộc tính cho màu, vì văn bản tiêu đề được gán sẽ không sử dụng màu bạn đã đặt bằng setTitleColor: forState:
daveMac

2
Tuyệt vời, và cảm ơn @daveMac cho những cái đầu lên màu. Đối với những người không biết thuộc tính đó là: NSForegroundColorAttributionName
Ryan Crews

trong phương pháp này, nút gạch chân gần với văn bản bất kỳ phương pháp nào để thay đổi vị trí y của gạch chân?
I Meat P

49

Bạn có thể làm điều đó trong chính trình xây dựng giao diện.

  1. Chọn thanh tra thuộc tính
  2. Thay đổi loại tiêu đề từ đơn giản sang quy kết

nhập mô tả hình ảnh ở đây

  1. Đặt kích thước phông chữ phù hợp và căn chỉnh văn bản

nhập mô tả hình ảnh ở đây

  1. Sau đó chọn văn bản tiêu đề và đặt phông chữ như gạch chân

nhập mô tả hình ảnh ở đây


28

Nó rất đơn giản với chuỗi được gán

Tạo một từ điển với các thuộc tính được thiết lập và áp dụng cho chuỗi được gán. Sau đó, bạn có thể đặt chuỗi được gán là attibutingtitle trong uibutton hoặc Attributiontext trong 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];

Cái gì commentString; bạn đã sao chép từ câu trả lời của @ NickH247 chưa?
ý nghĩa-vấn đề

21

Đây là chức năng của tôi, hoạt động trong 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)
}

CẬP NHẬT tiện ích mở rộng 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

Câu trả lời của Nick là một cách tuyệt vời, nhanh chóng để làm điều này.

Tôi đã thêm hỗ trợ drawRectcho bóng tối.

Câu trả lời của Nick không tính đến nếu tiêu đề nút của bạn có bóng bên dưới văn bản:

nhập mô tả hình ảnh ở đây

Nhưng bạn có thể di chuyển phần gạch chân xuống theo chiều cao của bóng như vậy:

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

Sau đó, bạn sẽ nhận được một cái gì đó như thế này:

nhập mô tả hình ảnh ở đây


self.titleLabel.font.descender; điều này đã bị khấu hao trong iOS 3.0
KING

5

Đối với Swift 3, có thể sử dụng tiện ích mở rộng sau:

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

Mở rộng câu trả lời của @Nick H247, tôi gặp phải một vấn đề trong đó trước tiên, phần gạch chân không được vẽ lại khi nút thay đổi kích thước khi xoay; điều này có thể được giải quyết bằng cách đặt nút của bạn để vẽ lại như vậy:

myButton.contentMode = UIViewContentModeRedraw; 

Điều này buộc nút phải vẽ lại khi giới hạn thay đổi.

Thứ hai, mã ban đầu giả sử bạn chỉ có 1 dòng văn bản trong nút (nút của tôi bao bọc thành 2 dòng khi xoay) và phần gạch chân chỉ xuất hiện trên dòng văn bản cuối cùng. Mã drawRect có thể được sửa đổi để đầu tiên tính toán số lượng dòng trong nút, sau đó đặt một gạch chân trên mỗi dòng thay vì chỉ dưới cùng, như vậy:

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

}


}

Hy vọng mã này sẽ giúp người khác!


3

Trong nhanh chóng

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

Bạn có thể sử dụng mã này để thêm gạch chân với khoảng cách trong nút.

  • Khi tôi cố gắng vẽ một gạch chân từ trình xây dựng giao diện. Nó trông giống như hình ảnh dưới đây.

1 - Tham chiếu trình xây dựng giao diện

nhập mô tả hình ảnh ở đây

  • Và sau khi sử dụng mã dưới đây, tôi đã đạt được kết quả như tôi muốn.

2 - sử dụng mã được mô tả

nhập mô tả hình ảnh ở đây

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

Cảm ơn bạn vì đoạn mã này, có thể cung cấp một số trợ giúp hạn chế, ngay lập tức. Một lời giải thích phù hợp sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách chỉ ra lý do tại sao đây là một giải pháp tốt cho vấn đề và sẽ giúp nó hữu ích hơn cho những người đọc tương lai với những câu hỏi tương tự khác. Vui lòng chỉnh sửa câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện.
Toby Speight

3

Phiên bản Swift 5.0 hoạt động kể từ tháng 9 năm 2019 trong 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)
  }
}

Để sử dụng nó, hãy đặt tiêu đề nút của bạn trước button.setTitle("Button Title", for: .normal)và sau đó gọi button.underlineText()để làm tiêu đề đó được gạch chân.


1
Tôi có thể xác nhận tính năng này hoạt động trên các phiên bản cũ như iOS 10.3.1, Xcode 10.3 không hỗ trợ trình giả lập cũ hơn Mojave, theo như tôi biết.
Max Desiatov

2

Làm thế nào một người sẽ xử lý trường hợp khi chúng ta giữ một nút nhấn gạch dưới? Trong trường hợp đó, màu văn bản của nút thay đổi theo màu được tô sáng nhưng dòng vẫn giữ nguyên màu gốc. Giả sử nếu màu văn bản nút ở trạng thái bình thường là màu đen thì phần gạch chân của nó cũng sẽ có màu đen. Màu nổi bật của nút là màu trắng. Nhấn giữ nút thay đổi màu văn bản nút từ đen sang trắng nhưng màu gạch chân vẫn là màu đen.


2
Bạn có thể kiểm tra nếu nút được tô sáng và hoặc được chọn, và đặt màu cho phù hợp. không chắc chắn nếu vẽ lại sẽ được yêu cầu tự động, nếu không, bạn sẽ cần ghi đè setSelected / setHighlighted và gọi super và [self setNeedDisplay]
Nick H247

2

Tôi tin rằng đó là một số lỗi trong trình soạn thảo phông chữ trong XCode. Nếu bạn sử dụng trình tạo giao diện, bạn phải thay đổi tiêu đề từ Đồng bằng sang Thuộc tính, mở TextEdit tạo văn bản được gạch chân và sao chép-dán vào hộp văn bản trong XCode


2

Câu trả lời của Nick H247 nhưng cách tiếp cận 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

Phiên bản Swift 3 cho câu trả lời của @ NickH247 với màu gạch chân, băng thông và khoảng cách gạch chân tùy chỉnh:

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")
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.