Hacer enlace en UILabel.Texto atribuido* no * azul y * no * subrayado


Quiero que algunas palabras dentro de mi OHAttributedLabel sean enlaces, pero quiero que sean colores distintos del azul y no quiero el subrayado.

Esto me da un enlace azul con texto subrayado:

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

}

Dado que estoy usando OHAttributedLabel, también intenté usar los métodos en su categoría NSAttributedString+Attributes.h, pero también devuelven enlaces subrayados azules:

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

Si comento la línea que establece los enlaces en cada versión, el texto se colorea a lo que paso, eso funciona. Simplemente parece que establecer el enlace está anulando esto y volviéndolo a azul.

Desafortunadamente, la página de Apple docs que encontré muestra cómo configurar el texto del enlace en azul y subrayarlo, exactamente lo que no necesito: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html

Author: Cœur, 2013-03-13

10 answers

Así que terminé usando 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];
}

Encontré que OHAttributedLabel realmente tiene métodos para establecer enlaces y declarar colores y subrayar estilos para esos enlaces. Sin embargo, quería que los enlaces fueran de diferentes colores basados en un parámetro. TTTAttributedLabel permite esto al permitirle establecer su propiedad linkAttributes para cada enlace que cree.

 61
Author: Ramsel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-02 21:55:43

Estoy usando TTTAttributedLabel. Quería cambiar el color del texto vinculado y mantenerlo subrayado. La respuesta de Pim se veía genial, pero no funcionó para mí. Esto es lo que funcionó:

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

Nota: si no desea que el texto esté subrayado, elimine la clave kCTUnderlineStyleAttributeName del diccionario.

 23
Author: kwahn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-04 14:17:44

Aquí está mi versión mejorada de la ya gran respuesta de Ramsel. Creo que es mucho más legible, y espero que llegue a buen uso.

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

Aquí está una lista de otros nombres de attibute.

 22
Author: Pim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-24 00:55:14

Si está utilizando un uitextview, es posible que tenga que cambiar el color del tintColor para cambiar el color del enlace

 8
Author: Lucas Chwe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-07 06:52:46

Ejemplo de Swift 2.3 para TTTAttributedLabel:

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

Swift 4

yourLabel.linkAttributes = [
    .foregroundColor: UIColor.grayColor(),
    .underlineStyle: NSNumber(value: true)
]
yourLabel.activeLinkAttributes = [
    .foregroundColor: UIColor.grayColor().withAlphaComponent(0.7),
    .underlineStyle: NSNumber(value: false)
]
 6
Author: pchelnikov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-19 16:43:21

Swift 3 ejemplo para TTTAttributedLabel:

yourLabel.linkAttributes = [
    NSForegroundColorAttributeName: UIColor.green,  
    NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleNone.rawValue)
]
yourLabel.activeLinkAttributes = [
    NSForegroundColorAttributeName: UIColor.green,  
    NSUnderlineStyleAttributeName: NSNumber(value: NSUnderlineStyle.styleDouble.rawValue)
]
 0
Author: Dana Wheeler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 11:38:59

Para Swift 3 usando 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)
 0
Author: pableiros,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-08 17:21:06

Para Swift 3 la siguiente manera funcionó para mí usando TTTAttributedLabel:

1) Agregue una etiqueta en el guion gráfico y defina su clase como TTTAttributedLabel Vista de Storyboard

2) En code define @IBOutlet var termsLabel: TTTAttributedLabel!

3) Luego en ViewDidLoad escriba estas líneas

let textString = "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

Se vería así Vista del Simulador

4) Finalmente escriba la función delegate de TTTAttributedLabel para que pueda abrir enlaces en tap

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
    }
}
 0
Author: Tul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-01 15:08:41

Swift 3:

// apply link attributes to label.attributedString, then
label.tintColor = UIColor.red // any color you want
 0
Author: Alexander Volkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-01 22:35:12

Si eres como yo y realmente no quieres usar TTT (o lo necesitas en tu propia implementación personalizada donde estás dibujando de otras maneras extrañas):

Primero, subclase NSLayoutManager y luego sobreescriba de la siguiente manera:

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

Esto es más o menos decirle al administrador de maquetación que realmente respete NSForegroundColorAttributeName de su cadena atribuida siempre en lugar de la rareza que Apple tiene internamente para los enlaces.

Si todo lo que necesita hacer es obtener un administrador de diseño que dibuja correctamente (como I needed), puedes parar aquí. Si usted necesita un realmente UILabel, es doloroso pero posible.

Primero, de nuevo, subclase UILabel y slap en todos estos métodos.

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

Descaradamente tomado de aquí. Increíblemente trabajo por parte del autor original para resolver todo esto.

 0
Author: Sirens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-19 05:57:32