Сделать ссылку в UILabel.attributedText *не* синей и *не* подчеркнутой

Я хочу, чтобы некоторые слова в моем OHAttributedLabel были ссылками, но я хочу, чтобы они были другого цвета, кроме синего, и мне не нужно подчеркивание.

Это дает мне синюю ссылку с подчеркнутым текстом:

 -(void)createLinkFromWord:(NSString*)word withColor:(UIColor*)color atRange:(NSRange)range{

    NSMutableAttributedString* mutableAttributedText = [self.label.attributedText mutableCopy];   

    [mutableAttributedText beginEditing];
    [mutableAttributedText addAttribute:kOHLinkAttributeName
                   value:[NSURL URLWithString:@"http://www.somewhere.net"]
                   range:range];

    [mutableAttributedText addAttribute:(id)kCTForegroundColorAttributeName
                   value:color
                   range:range];

    [mutableAttributedText addAttribute:(id)kCTUnderlineStyleAttributeName
                   value:[NSNumber numberWithInt:kCTUnderlineStyleNone]
                   range:range];
    [mutableAttributedText endEditing];


    self.label.attributedText = mutableAttributedText;

}

Поскольку я использую OHAttributedLabel, я также пытался использовать методы в его категории NSAttributedString+Attributes.h, но они также возвращают подчеркнутые синим цветом ссылки:

-(void)createLinkFromWord:(NSString*)word withColor:(UIColor*)color atRange:(NSRange)range{

NSMutableAttributedString* mutableAttributedText = [self.label.attributedText mutableCopy];

[mutableAttributedText setLink:[NSURL URLWithString:@"http://www.somewhere.net"] range:range];
[mutableAttributedText setTextColor:color range:range];
[mutableAttributedText setTextUnderlineStyle:kCTUnderlineStyleNone range:range];

self.label.attributedText = mutableAttributedText;
}

Если я закомментирую строку, устанавливающую ссылки в каждой версии, текст будет окрашен в соответствии с тем, что я передаю - это работает. Просто кажется, что установка ссылки отменяет это и возвращает ее к синему цвету.

К сожалению, на странице документов Apple, которую я нашел, показано, как сделать текст ссылки синим цветом и подчеркнуть его, а именно то, что мне не нужно: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html


person OdieO    schedule 13.03.2013    source источник


Ответы (11)


В итоге я использовал TTTAttributedLabel:

-(void)createLinkFromWord:(NSString*)word withColor:(UIColor*)color atRange:(NSRange)range{
    NSMutableAttributedString* newTextWithLinks = [self.label.attributedText mutableCopy];
    NSURL *url = [NSURL URLWithString:@"http://www.reddit.com"];
    self.label.linkAttributes = @{NSForegroundColorAttributeName: color, 
                                   NSUnderlineStyleAttributeName: @(NSUnderlineStyleNone)};
    [self.label addLinkToURL:url withRange:range];
}

Я обнаружил, что OHAttributedLabel на самом деле имеет методы для установки ссылок и объявления цветов и стилей подчеркивания для этих ссылок. Однако я хотел, чтобы ссылки были разных цветов в зависимости от параметра. TTTAttributedLabel позволяет это сделать, позволяя вам установить свойство linkAttributes для каждой создаваемой вами ссылки.

person OdieO    schedule 19.03.2013
comment
Также не забудьте установить linkAttributes перед добавлением ссылки на метку. - person damirstuhec; 18.05.2015
comment
Нет ли другого способа установить цвет ссылки, кроме как с каким-либо проектом github? свойство linkAttributes недоступно для UILabel. - person TheJeff; 26.08.2016
comment
Я не вижу, где вы используете аргумент слова и переменную newTextWithLinks. - person Hola Soy Edu Feliz Navidad; 25.03.2021

Я использую TTTAttributedLabel. Я хотел изменить цвет связанного текста и сохранить его подчеркнутым. Ответ Пима выглядел великолепно, но не сработал для меня. Вот что сработало:

label.linkAttributes = @{ (id)kCTForegroundColorAttributeName: [UIColor magentaColor],
                           (id)kCTUnderlineStyleAttributeName : [NSNumber numberWithInt:NSUnderlineStyleSingle] };

Примечание: если вы не хотите, чтобы текст подчеркивался, удалите ключ kCTUnderlineStyleAttributeName из словаря.

person kwahn    schedule 04.03.2015
comment
Здорово! Это работает и для меня. Но других ответов нет. - person Accid Bright; 11.12.2015
comment
Да, только этот ответ работает! Использование NSForegroundColorAttributeName не имеет смысла. Вы должны использовать kCTForegroundColorAttributeName. См. эту проблему. - person flashlib; 23.03.2016

Вот моя улучшенная версия уже отличного ответа Рамселя. Я считаю, что это гораздо более читабельно, и я надеюсь, что это найдет хорошее применение.

label.linkAttributes = @{ NSForegroundColorAttributeName: [UIColor whiteColor],
                          NSUnderlineStyleAttributeName: [NSNumber numberWithInt:NSUnderlineStyleSingle] };

Вот список имен других атрибутов .

person Pim    schedule 27.06.2014
comment
UILabel не имеет свойства linkAttributes. - person Softlion; 24.05.2019
comment
Правильный @Softlion, но TTTAttributedLabel делает. По крайней мере, так было 4 года назад. Я понятия не имею, хорош ли этот ответ. - person Pim; 24.05.2019

Если вы используете UITextView, возможно, вам придется изменить свойство tintColor, чтобы изменить цвет ссылки.

person Lucas Chwe    schedule 07.05.2015

Если вы похожи на меня и действительно не хотите использовать TTT (или нуждаетесь в нем в своей собственной реализации, где вы рисуете другими странными способами):

Сначала создайте подкласс NSLayoutManager, а затем переопределите его следующим образом:

- (void)showCGGlyphs:(const CGGlyph *)glyphs 
           positions:(const CGPoint *)positions 
               count:(NSUInteger)glyphCount
                font:(UIFont *)font 
              matrix:(CGAffineTransform)textMatrix 
          attributes:(NSDictionary *)attributes
           inContext:(CGContextRef)graphicsContext
{
   UIColor *foregroundColor = attributes[NSForegroundColorAttributeName];

   if (foregroundColor)
   {
      CGContextSetFillColorWithColor(graphicsContext, foregroundColor.CGColor);
   }

   [super showCGGlyphs:glyphs 
             positions:positions 
                 count:glyphCount 
                  font:font 
                matrix:textMatrix 
            attributes:attributes 
             inContext:graphicsContext];
}

Это более или менее указывает менеджеру по расположению на самом деле уважать NSForegroundColorAttributeName из вашей атрибутированной строки всегда вместо странностей, которые Apple имеет внутри для ссылок.

Если все, что вам нужно сделать, это получить менеджер компоновки, который правильно рисует (как мне нужно), вы можете остановиться здесь. Если вам действительно нужен UILabel, это болезненно, но возможно.

Во-первых, снова создайте подкласс UILabel и добавьте все эти методы.

- (NSTextStorage *)textStorage
{
   if (!_textStorage)
   {
      _textStorage = [[NSTextStorage alloc] init];
      [_textStorage addLayoutManager:self.layoutManager];
      [self.layoutManager setTextStorage:_textStorage];
   }

   return _textStorage;
}

- (NSTextContainer *)textContainer
{
   if (!_textContainer)
   {
      _textContainer = [[NSTextContainer alloc] init];
      _textContainer.lineFragmentPadding = 0;
      _textContainer.maximumNumberOfLines = self.numberOfLines;
      _textContainer.lineBreakMode = self.lineBreakMode;
      _textContainer.widthTracksTextView = YES;
      _textContainer.size = self.frame.size;

      [_textContainer setLayoutManager:self.layoutManager];
   }

   return _textContainer;
}

- (NSLayoutManager *)layoutManager
{
   if (!_layoutManager)
   {
      // Create a layout manager for rendering
      _layoutManager = [[PRYLayoutManager alloc] init];
      _layoutManager.delegate = self;
      [_layoutManager addTextContainer:self.textContainer];
   }

   return _layoutManager;
}

- (void)layoutSubviews
{
   [super layoutSubviews];

   // Update our container size when the view frame changes
   self.textContainer.size = self.bounds.size;
}

- (void)setFrame:(CGRect)frame
{
   [super setFrame:frame];

   CGSize size = frame.size;
   size.width = MIN(size.width, self.preferredMaxLayoutWidth);
   size.height = 0;
   self.textContainer.size = size;
}

- (void)setBounds:(CGRect)bounds
{
   [super setBounds:bounds];

   CGSize size = bounds.size;
   size.width = MIN(size.width, self.preferredMaxLayoutWidth);
   size.height = 0;
   self.textContainer.size = size;
}

- (void)setPreferredMaxLayoutWidth:(CGFloat)preferredMaxLayoutWidth
{
   [super setPreferredMaxLayoutWidth:preferredMaxLayoutWidth];

   CGSize size = self.bounds.size;
   size.width = MIN(size.width, self.preferredMaxLayoutWidth);
   self.textContainer.size = size;
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
{
  // Use our text container to calculate the bounds required. First save our
  // current text container setup
  CGSize savedTextContainerSize = self.textContainer.size;
  NSInteger savedTextContainerNumberOfLines = self.textContainer.maximumNumberOfLines;

  // Apply the new potential bounds and number of lines
  self.textContainer.size = bounds.size;
  self.textContainer.maximumNumberOfLines = numberOfLines;

  // Measure the text with the new state
  CGRect textBounds;
  @try
  {
      NSRange glyphRange = [self.layoutManager 
                            glyphRangeForTextContainer:self.textContainer];
      textBounds = [self.layoutManager boundingRectForGlyphRange:glyphRange
                                       inTextContainer:self.textContainer];

      // Position the bounds and round up the size for good measure
      textBounds.origin = bounds.origin;
      textBounds.size.width = ceilf(textBounds.size.width);
      textBounds.size.height = ceilf(textBounds.size.height);
  }
  @finally
  {
      // Restore the old container state before we exit under any circumstances
      self.textContainer.size = savedTextContainerSize;
      self.textContainer.maximumNumberOfLines = savedTextContainerNumberOfLines;
   }

   return textBounds;
}

- (void)setAttributedText:(NSAttributedString *)attributedText
{
    // Pass the text to the super class first
    [super setAttributedText:attributedText];

    [self.textStorage setAttributedString:attributedText];
}
- (CGPoint)_textOffsetForGlyphRange:(NSRange)glyphRange
{
   CGPoint textOffset = CGPointZero;

   CGRect textBounds = [self.layoutManager boundingRectForGlyphRange:glyphRange 
                                                     inTextContainer:self.textContainer];
   CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0f;
   if (paddingHeight > 0)
   {
       textOffset.y = paddingHeight;
   }

   return textOffset;
}

- (void)drawTextInRect:(CGRect)rect
{
   // Calculate the offset of the text in the view
   CGPoint textOffset;
   NSRange glyphRange = [self.layoutManager glyphRangeForTextContainer:self.textContainer];
   textOffset = [self _textOffsetForGlyphRange:glyphRange];

   // Drawing code
   [self.layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textOffset];

   // for debugging the following 2 line should produce the same results
   [self.layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textOffset];
   //[super drawTextInRect:rect];
}

Бесстыдно взято отсюда. Невероятная работа над оригинальной авторской частью для проработки всего этого.

person Allison    schedule 19.08.2018

Пример Swift 2.3 для TTTAttributedLabel:

yourLabel.linkAttributes       = [
    NSForegroundColorAttributeName: UIColor.grayColor(),
    NSUnderlineStyleAttributeName: NSNumber(bool: true)
]
yourLabel.activeLinkAttributes = [
    NSForegroundColorAttributeName: UIColor.grayColor().colorWithAlphaComponent(0.8),
    NSUnderlineStyleAttributeName: NSNumber(bool: false)
]

Свифт 4

yourLabel.linkAttributes = [
    .foregroundColor: UIColor.grayColor(),
    .underlineStyle: NSNumber(value: true)
]
yourLabel.activeLinkAttributes = [
    .foregroundColor: UIColor.grayColor().withAlphaComponent(0.7),
    .underlineStyle: NSNumber(value: false)
]
person pchelnikov    schedule 05.10.2016
comment
UILabel не имеет linkAttributes - person Ali Alaoi; 23.07.2020
comment
@AliAlaoi этот пример для TTTAttributedLabel - person pchelnikov; 24.07.2020

Для Swift 3 у меня сработал следующий способ, используя TTTAttributedLabel:

1) Добавьте метку на раскадровку и определите ее класс как TTTAttributedLabel Просмотр раскадровки

2) В коде определить @IBOutlet var termsLabel: TTTAttributedLabel!

3) Затем в ViewDidLoad пишем эти строчки

termsLabel.attributedText = NSAttributedString(string: "By using this app you agree to the Privacy Policy & Terms & Conditions.")
guard let labelString = termsLabel.attributedText else {
            return
        }
        guard let privacyRange = labelString.string.range(of: "Privacy Policy") else {
            return
        }
        guard let termsConditionRange = labelString.string.range(of: "Terms & Conditions") else {
            return
        }

        let privacyNSRange: NSRange = labelString.string.nsRange(from: privacyRange)
        let termsNSRange: NSRange = labelString.string.nsRange(from: termsConditionRange)

        termsLabel.addLink(to: URL(string: "privacy"), with: privacyNSRange)
        termsLabel.addLink(to: URL(string: "terms"), with: termsNSRange)

        termsLabel.delegate = self
        let attributedText = NSMutableAttributedString(attributedString: termsLabel.attributedText!)
        attributedText.addAttributes([NSFontAttributeName : UIFont(name: "Roboto-Medium", size: 12)!], range: termsNSRange)
        attributedText.addAttributes([NSFontAttributeName : UIFont(name: "Roboto-Medium", size: 12)!], range: privacyNSRange)
        attributedText.addAttributes([kCTForegroundColorAttributeName as String: UIColor.orange], range: termsNSRange)
        attributedText.addAttributes([kCTForegroundColorAttributeName as String: UIColor.green], range: privacyNSRange)
        attributedText.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.styleNone.rawValue], range: termsNSRange)
        attributedText.addAttributes([NSUnderlineStyleAttributeName: NSUnderlineStyle.styleNone.rawValue], range: privacyNSRange)
        termsLabel.attributedText = attributedText

Это будет выглядеть так: Вид симулятора

4) Наконец, напишите функцию делегата TTTAttributedLabel, чтобы вы могли открывать ссылки по тапу.

public func attributedLabel(_ label: TTTAttributedLabel!, didSelectLinkWith url: URL!) {
    switch url.absoluteString  {
    case "privacy":
        SafariBrowser.open("http://google.com", presentingViewController: self)
    case "terms":
        SafariBrowser.open("http://google.com", presentingViewController: self)
    default:
        break
    }
}

Обновление для Swift 4.2

Для Swift 4.2 в шаге 3 есть некоторые изменения, все остальные шаги останутся такими же, как указано выше:

3) В ViewDidLoad напишите эти строки

termsLabel.attributedText = NSAttributedString(string: "By using this app you agree to the Privacy Policy & Terms & Conditions.")
guard let labelString = termsLabel.attributedText else {
            return
        }
        guard let privacyRange = labelString.string.range(of: "Privacy Policy") else {
            return
        }
        guard let termsConditionRange = labelString.string.range(of: "Terms & Conditions") else {
            return
        }

        let privacyNSRange: NSRange = labelString.string.nsRange(from: privacyRange)
        let termsNSRange: NSRange = labelString.string.nsRange(from: termsConditionRange)

        termsLabel.addLink(to: URL(string: "privacy"), with: privacyNSRange)
        termsLabel.addLink(to: URL(string: "terms"), with: termsNSRange)

        termsLabel.delegate = self
        let attributedText = NSMutableAttributedString(attributedString: termsLabel.attributedText!)
        attributedText.addAttributes([NSAttributedString.Key.font : UIFont(name: "Roboto-Regular", size: 12)!], range: termsNSRange)
        attributedText.addAttributes([NSAttributedString.Key.font : UIFont(name: "Roboto-Regular", size: 12)!], range: privacyNSRange)
        attributedText.addAttributes([kCTForegroundColorAttributeName as NSAttributedString.Key : UIColor.orange], range: termsNSRange)
        attributedText.addAttributes([kCTForegroundColorAttributeName as NSAttributedString.Key : UIColor.green], range: privacyNSRange)
    attributedText.addAttributes([NSAttributedString.Key.underlineStyle: 0], range: termsNSRange)
        attributedText.addAttributes([NSAttributedString.Key.underlineStyle: 0], range: privacyNSRange)
person Tul    schedule 01.09.2017
comment
у вас есть быстрое решение 4.0? Я не получаю несколько цветов в ссылке. - person guru; 09.02.2019

Пример Swift 3 для TTTAttributedLabel:

yourLabel.linkAttributes = [
    NSForegroundColorAttributeName: UIColor.green,  
    NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleNone.rawValue)
]
yourLabel.activeLinkAttributes = [
    NSForegroundColorAttributeName: UIColor.green,  
    NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleDouble.rawValue)
]
person Dana Wheeler    schedule 23.12.2016

Для Swift 3 с использованием TTTAttributedLabel

let title: NSString = "Fork me on GitHub!"

var attirutedDictionary = NSMutableDictionary(dictionary:attributedLabel.linkAttributes)

attirutedDictionary[NSForegroundColorAttributeName] = UIColor.red
attirutedDictionary[NSUnderlineStyleAttributeName] =  NSNumber(value: NSUnderlineStyle.styleNone.rawValue)

attributedLabel.attributedText = NSAttributedString(string: title as String)
attributedLabel.linkAttributes = attirutedDictionary as! [AnyHashable: Any]

let range = subtitleTitle.range(of: "me")
let url = URL(string: "http://github.com/mattt/")

attributedLabel.addLink(to: url, with: range)
person pableiros    schedule 08.03.2017

Свифт 4.0:

Коротко и просто

 let LinkAttributes = NSMutableDictionary(dictionary: testLink.linkAttributes)
 LinkAttributes[NSAttributedStringKey.underlineStyle] =  NSNumber(value: false)
 LinkAttributes[NSAttributedStringKey.foregroundColor] = UIColor.black // Whatever your label color
 testLink.linkAttributes = LinkAttributes as NSDictionary as! [AnyHashable: Any]
person guru    schedule 19.01.2019
comment
UILabel не имеет linkAttributes - person Softlion; 24.05.2019

Лучше использовать UITextView с включенной функцией "Ссылка". В этом случае вы можете сделать это одной строкой:

Свифт 4:

// apply link attributes to label.attributedString, then
textView.tintColor = UIColor.red // any color you want

Полный пример:

    let attributedString = NSMutableAttributedString(string: "Here is my link")
    let range = NSRange(location: 7, length:4)
    attributedString.addAttribute(.link, value: "http://google.com", range: range)
    attributedString.addAttribute(.underlineStyle, value: 1, range: range)
    attributedString.addAttribute(.underlineColor, value: UIColor.red, range: range)
    textView.tintColor = UIColor.red // any color you want

Или вы можете применить атрибуты только к ссылкам:

  textView.linkTextAttributes = [
      .foregroundColor: UIColor.red
      .underlineStyle: 1,
      .underlineColor: UIColor.red
   ]
person Alexander Volkov    schedule 01.09.2017
comment
Это не работает, цвет ссылки точно такой же. На чем ты это используешь? - person Allison; 20.08.2018
comment
@Сирены Извините. На самом деле, я использовал его для textView. Я отредактировал ответ выше. - person Alexander Volkov; 05.03.2019