UIButton'da metnin altını çizme


143

Herkes bir UIButton unvanının altını çizmeyi önerebilir mi? Özel tipte bir UIButton'um var ve Başlığın altı çizili olmasını istiyorum, ancak Interface Builder bunu yapmak için herhangi bir seçenek sunmuyor.

Bir Düğme için Yazı Tipi Seçeneğini seçtiğinizde Arabirim Oluşturucu'da Yok, Tek, Çift, Renkli seçeneğini belirleme seçeneği sunar, ancak bunların hiçbiri Düğme üzerindeki Başlık üzerinde herhangi bir değişiklik sağlamaz.

Herhangi bir yardım takdir.


1
Bu soru olarak kendisine bir bağlantı ekleyerek atfedilen dize ile UITextView kullanabilirsiniz stackoverflow.com/questions/21629784/...
Halid Annajar

Yanıtlar:


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
belki ihtiyacınız kadar zamanında değil!
Nick H247

4
Teşekkürler, ben şu şekilde çağırdı: UIButton * btn = [UIUnderlinedButton buttonWithType: UIButtonTypeCustom];
hb922

2
Kod iyi çalışıyor, ancak görünüm döndürme drawRectçağrılmaması nedeniyle döndürme boyutu değiştiğinde alt çizgi küçültmek / büyümek fark ettim . Bu, düğmenizi şu şekilde yeniden çizecek şekilde ayarlayarak çözülebilir: myButton.contentMode = UIViewContentModeRedraw;sınırlar değiştiğinde düğmeyi yeniden çizmeye zorlar.
AndroidNoob

4
Ayrıca setTitleyöntemi şöyle de geçersiz kılabilirsiniz :objective-c - (void)setTitle:(NSString *)title forState:(UIControlState)state { [super setTitle:title forState:state]; [self setNeedsDisplay]; }
Kirualex

379

Altını çizmek üzere arabirim oluşturucuyu kullanmak için aşağıdakiler gerekir:

  • İlişkilendirilmiş olarak değiştirin
  • Özellikler denetçisindeki metni vurgulayın
  • Sağ tıklayın, Yazı Tipi'ni ve ardından Altı Çizili'yi seçin

IB kullanarak altını çiz

Başka birinin yaptığı videoyu https://www.youtube.com/watch?v=5-ZnV3jQd9I


2
İyi soru @ new2ios Belki de başkası bilir
finneycanhelp

1
Yeni bir soru soracağım, @finneycanhelp. Umarım Xcode 6.3'te daha kolay bir yol olacaktır. Yani çözümünüzü ayarlayabilir ve sonra setTitleatfedilen metin ile kullanabilirsiniz . Benim için alt çizgi çizmek için özel düğme oluşturmak biraz egzotik (belki de bir Uygulama tamamlanmış olsa bile iOS için yeniyim).
new2ios

2
finneydonehelped! Bunun için teşekkürler! Yazı Tipleri açılır iletişim kutusunun neden hiçbir etkisi olmadığını anlayamadık. Sağ tıklama mükemmel.
IMFletcher

2
Interface Builder kullanıcıları için, Kod yaparken biraz çalışma olan bu tür basit şeyler için iyi bir cevap. Teşekkürler! (Y)
Randika Vishman

1
İOS geliştiricileri neden sadece çok basit bir sorun için çok uzun bir kod yazmayı tercih ediyor?
mr5

129

İOS6'dan, artık daha esnek bir şekilde alt çizgi (ve ilişkilendirilen dizelerin desteklediği herhangi bir şey) gerçekleştirmek için bir NSAttributedString kullanmak mümkündür:

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

Not: bunu başka bir cevap olarak ekledi - öncekime tamamen farklı bir çözüm.

Düzenle: garip bir şekilde (en azından iOS8'de) ilk karakterin altını çizmelisiniz, aksi takdirde işe yaramaz!

geçici bir çözüm olarak, ilk karakterin altını net renkle ayarlayın!

    // 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
Sadece bu şekilde yaptığınızda, renk için nitelik de eklemeniz gerektiğini unutmayın, çünkü atıfta bulunulan başlık metni setTitleColor: forState kullanarak ayarladığınız rengi kullanmayacaktır:
daveMac

2
Harika, ve renk üzerinde kafaları için @daveMac teşekkürler. Özelliği bilmeyenler için: NSForegroundColorAttributeName
Ryan Crews

bu yöntemlerde alt çizgi düğmesi alt çizginin y konumunu değiştirmek için herhangi bir yöntem metne yakın?
Ilesh P

49

Bunu arayüz oluşturucunun kendisinde yapabilirsiniz.

  1. Özellik denetçisini seçin
  2. Başlık türünü düzden atfedilene değiştir

resim açıklamasını buraya girin

  1. Uygun yazı tipi boyutunu ve metin hizalamasını ayarlama

resim açıklamasını buraya girin

  1. Ardından başlık metnini seçin ve yazı tipini altı çizili olarak ayarlayın

resim açıklamasını buraya girin


28

Atfedilen dize ile çok basit

Ayarlanmış özniteliklere sahip bir sözlük oluşturur ve ilişkilendirilmiş dizeye uygulanır. Ardından, ilişkilendirilmiş dizeyi uibutton'da attibutedtitle veya uilabel'de attributedtext olarak ayarlayabilirsiniz.

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

Nedir commentString; @ NickH247'nin cevabından kopyaladın mı?
Anlamı önemli

21

İşte benim fonksiyonum, Swift 1.2'de çalışıyor.

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 uzantısı:

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

Nick'in cevabı bunu yapmanın harika ve hızlı bir yoludur.

drawRectGölgelere destek ekledim .

Düğme başlığınızın metnin altında bir gölge varsa Nick'in cevabı dikkate alınmaz:

resim açıklamasını buraya girin

Ancak alt çizgiyi gölgenin yüksekliğine göre aşağıdaki gibi hareket ettirebilirsiniz:

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

Sonra böyle bir şey alırsınız:

resim açıklamasını buraya girin


self.titleLabel.font.descender; Bu, iOS 3.0'da amortismana tabi
KING

5

Swift 3 için aşağıdaki uzantı kullanılabilir:

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'nin cevabına genişlerken, düğme dönme sırasında yeniden boyutlandırıldığında öncelikle alt çizginin yeniden çizilmediği bir sorunla karşılaştım; bu, düğmenizi şu şekilde yeniden çizecek şekilde ayarlayarak çözülebilir:

myButton.contentMode = UIViewContentModeRedraw; 

Bu, sınırlar değiştiğinde düğmeyi yeniden çizmeye zorlar.

İkincisi, orijinal kod düğmede sadece 1 satır metin olduğunu varsaydı (düğmem rotasyonda 2 satıra sarar) ve alt çizgi yalnızca metnin son satırında görünür. DrawRect kodu önce düğmedeki satır sayısını hesaplamak için değiştirilebilir, ardından aşağıdaki gibi yalnızca alttan ziyade her satıra bir alt çizgi koyabilirsiniz:

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

}


}

Umarım bu kod başka birine yardımcı olur!


3

Hızlı

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

Bu kodu, düğmedeki boşluklarla alt çizgi eklemek için kullanabilirsiniz.

  • Arayüz oluşturucudan bir alt çizgi çizmeye çalıştığımda. Aşağıdaki görüntü gibi görünüyor.

1 - Arayüz oluşturucu referansı

resim açıklamasını buraya girin

  • Ve aşağıdaki kodu kullandıktan sonra istediğim gibi sonuç elde ettim.

2 - açıklanan kodu kullanarak

resim açıklamasını buraya girin

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

Sınırlı ve anında yardım sağlayabilecek bu kod snippet'i için teşekkür ederiz. Uygun bir açıklama , bunun neden problem için iyi bir çözüm olduğunu göstererek uzun vadeli değerini büyük ölçüde artıracak ve diğer benzer sorularla gelecekteki okuyucular için daha yararlı hale getirecektir. Yaptığınız varsayımlar dahil bazı açıklamalar eklemek için lütfen yanıtınızı düzenleyin .
Toby Speight

3

Xcode 10.3'te Eylül 2019'dan itibaren çalışan Swift 5.0 sürümü:

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

Bunu kullanmak için önce düğme başlığınızı ayarlayın button.setTitle("Button Title", for: .normal)ve sonra button.underlineText()bu başlığın altını çizmek için arayın .


1
Bunun iOS 10.3.1 gibi eski sürümlerde çalıştığını doğrulayabilirim, Xcode 10.3, bildiğim kadarıyla Mojave'den daha eski simülatörleri desteklemiyor.
Max Desiatov

2

Altı çizili bir düğmeyi basılı tuttuğumuzda durum nasıl ele alınacak? Bu durumda, düğmenin metin rengi vurgulanan renge göre değişir, ancak çizgi orijinal renkte kalır. Diyelim ki normal durumdaki düğme metni rengi siyahsa, alt çizgisinin de siyah rengi olacaktır. Düğmenin vurgulanan rengi beyazdır. Düğmeyi basılı tutmak düğme metin rengini siyahtan beyaza değiştirir, ancak altı çizili renk siyah kalır.


2
Düğmenin vurgulanıp vurgulanmadığını ve / veya seçilip seçilmediğini test edebilir ve rengi buna göre ayarlayabilirsiniz. yeniden çizimin otomatik olarak istenip istenmeyeceğinden emin değilseniz, değilse setSelected / setHighlighted öğesini geçersiz kılmanız ve super ve [self setNeedsDisplay] öğelerini geçersiz kılmanız gerekir
Nick H247

2

XCode yazı tipi düzenleyicisinde bir hata olduğuna inanıyorum. Arayüz oluşturucuyu kullanıyorsanız, başlığı Düz'den İlişkilendirilmiş olarak değiştirmeniz gerekir, TextEdit'i altı çizili metin oluştur ve XCode'da metin kutusuna kopyalayıp yapıştır


2

Nick H247'nin cevabı ama Swift yaklaşımı:

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

Özel altı çizili renk, çizgi genişliği ve boşluk ile @ NickH247'nin cevabı için Swift 3 sürümü :

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")
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.