¿Cómo puedo hacer un enlace en el que se pueda hacer clic en una NSAttributedString?

200

Es trivial hacer que se pueda hacer clic en hipervínculos en a UITextView. Simplemente configura la casilla de verificación "detectar enlaces" en la vista en IB, y detecta enlaces HTTP y los convierte en hipervínculos.

Sin embargo, eso todavía significa que lo que el usuario ve es el enlace "en bruto". Los archivos RTF y HTML le permiten configurar una cadena legible por el usuario con un enlace "detrás".

Es fácil instalar texto atribuido en una vista de texto (o una UILabelo UITextField, para el caso). Sin embargo, cuando ese texto atribuido incluye un enlace, no se puede hacer clic en él.

¿Hay una manera de hacer que el usuario puede hacer clic legible texto en una UITextView, UILabelo UITextField?

El marcado es diferente en SO, pero aquí está la idea general. Lo que quiero es un texto como este:

Esta transformación se generó con Face Dancer , haga clic para ver en la tienda de aplicaciones.

Lo único que puedo obtener es esto:

Esta transformación se generó con Face Dancer. Haga clic en http://example.com/facedancer para verla en la tienda de aplicaciones.

Duncan C
fuente
Pruebe esta muestra ... IFTweetLabel Espero que ayude ...
Vidhyanand
Buen trabajo pasando los 100K en un abrir y cerrar de ojos. Bienvenido al club 100K. ¡Bien merecido!
vacawama
@vacawama, espera, ¿cuándo sucedió eso? ¡Estaba en ≈98k la última vez que miré! (Escucho rumores de que obtienes un poco de botín SO como miembro del club 100k?)
Duncan C
Cambiaron los votos a favor en las preguntas de +5 a +10, por lo que si tuviera 800 votos a favor obtendría +4000 en un instante. Todavía estoy esperando un botín de 100k (cruzado en abril). Algo sobre el cambio de vendedores de botín ...
vacawama

Respuestas:

156

Use NSMutableAttributedString .

NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"];
[str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)];
yourTextView.attributedText = str;

Editar :

No se trata directamente de la pregunta, sino solo para aclarar, UITextFieldy UILabelno admite la apertura de URL. Si desea usar UILabelcon enlaces, puede verificar TTTAttributedLabel .

También debe establecer dataDetectorTypesel valor de la UITextViewa UIDataDetectorTypeLink, o UIDataDetectorTypeAllal abrir las URL cuando se hace clic. O puede usar el método delegado como se sugiere en los comentarios.

ujell
fuente
77
Sí, está funcionando, simplemente póngalo dentro de un UITextView y anule el método delegado: - (BOOL) textView: (UITextView *) textView shouldInteractWithURL: (NSURL *) url inRange: (NSRange) characterRange
Yunus Nedim Mehel
Esto no funciona en un UILabel: no sucede nada cuando toca el campo.
Jack BeNimble
77
@saboehnke ¿te refieres a llamar a un método cuando se hace clic en el enlace? Si es así implementar el método delegado, dar un maniquí URL como atributo y llame a su método en- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
ujell
2
No sé cómo está funcionando. El valor del atributo debe ser tipo de NSURL. ----[str addAttribute: NSLinkAttributeName value: [NSURL URLWithString:@"http://www.google.com"] range: NSMakeRange(0, str.length)];
Nirav Dangi
1
@NiravDangi deNSAttributedString.h UIKIT_EXTERN NSString * const NSLinkAttributeName NS_AVAILABLE(10_0, 7_0); // NSURL (preferred) or NSString
Ahmed Nawar
143

Encontré esto realmente útil, pero necesitaba hacerlo en bastantes lugares, así que resumí mi enfoque en una extensión simple para NSMutableAttributedString:

Swift 3

extension NSMutableAttributedString {

    public func setAsLink(textToFind:String, linkURL:String) -> Bool {

        let foundRange = self.mutableString.range(of: textToFind)
        if foundRange.location != NSNotFound {
            self.addAttribute(.link, value: linkURL, range: foundRange)
            return true
        }
        return false
    }
}

Swift 2

import Foundation

extension NSMutableAttributedString {

   public func setAsLink(textToFind:String, linkURL:String) -> Bool {

       let foundRange = self.mutableString.rangeOfString(textToFind)
       if foundRange.location != NSNotFound {
           self.addAttribute(NSLinkAttributeName, value: linkURL, range: foundRange)
           return true
       }
       return false
   }
}

Ejemplo de uso:

let attributedString = NSMutableAttributedString(string:"I love stackoverflow!")
let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com")

if linkWasSet {
    // adjust more attributedString properties
}

C objetivo

Acabo de cumplir un requisito para hacer lo mismo en un proyecto puro de Objective-C, así que aquí está la categoría de Objective-C.

@interface NSMutableAttributedString (SetAsLinkSupport)

- (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL;

@end


@implementation NSMutableAttributedString (SetAsLinkSupport)

- (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL {

     NSRange foundRange = [self.mutableString rangeOfString:textToFind];
     if (foundRange.location != NSNotFound) {
         [self addAttribute:NSLinkAttributeName value:linkURL range:foundRange];
         return YES;
     }
     return NO;
}

@end

Ejemplo de uso:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"];

BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"];

if (linkWasSet) {
    // adjust more attributedString properties
}

Asegúrese de que el atributo de comportamiento del NSTextField esté configurado como Seleccionable. Atributo de comportamiento Xcode NSTextField

Karl Nosworthy
fuente
Un ejemplo rápido de uso / implementación de esto sería muy apreciado.
ioopl
3
@ioop. He agregado un ejemplo muy pequeño a la publicación original anterior, espero que ayude.
Karl Nosworthy
77
Esto funcionó correctamente. Solo quiero decir que debe hacer que su UITextView sea seleccionable para permitir que se pueda hacer clic en el enlace
lujop
1
@felecia genet, en las implementaciones de Objective C y Swift, el método devuelve un resultado booleano para indicar si se produjo una coincidencia y el conjunto resultante. El error que está viendo es porque no está capturando ese resultado, lo cual está bien. Puede capturar ese resultado asignándolo a una variable local o ajustar el método para evitar que devuelva el valor booleano si eso se adapta mejor a sus necesidades. ¿Espero que eso ayude?
Karl Nosworthy
1
No hay problema @feleciagenet, he agregado el almacenamiento y la comprobación del resultado del método a los ejemplos Swift y ObjectiveC.
Karl Nosworthy
34

Acabo de crear una subclase de UILabel para abordar especialmente estos casos de uso. Puede agregar múltiples enlaces fácilmente y definir diferentes manejadores para ellos. También admite resaltar el enlace presionado cuando toca hacia abajo para obtener retroalimentación táctil. Consulte https://github.com/null09264/FRHyperLabel .

En su caso, el código puede ser así:

FRHyperLabel *label = [FRHyperLabel new];

NSString *string = @"This morph was generated with Face Dancer, Click to view in the app store.";
NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]};

label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes];

[label setLinkForSubstring:@"Face Dancer" withLinkHandler:^(FRHyperLabel *label, NSString *substring){
    [[UIApplication sharedApplication] openURL:aURL];
}];

Captura de pantalla de muestra (el controlador está configurado para mostrar una alerta en lugar de abrir una URL en este caso)

bailarina

Jinghan Wang
fuente
si supongamos que mi texto es así, esta transformación se generó con Face Dancer, haga clic para ver la vista de Dancer en la tienda de aplicaciones Face Dancer. aquí estoy teniendo 3 Face Dancer no estaba funcionando para ello
MANCHIKANTI KRISHNAKISHORE
1
En este caso, utilice la API en su - (void)setLinkForRange:(NSRange)range withLinkHandler:(void(^)(FRHyperLabel *label, NSRange selectedRange))handler; lugar. Consulte el archivo Léame en la página de Github.
Jinghan Wang
1
FRHyperLabel parece que ya no funciona. Dentro de "characterIndexForPoint:", siempre devuelve -1 (no encontrado).
John Pang
No me funciona para la etiqueta multilínea. La detección de caracteres está mal. Se puede hacer clic en la cadena de enlace de 15 caracteres solo en algunos primeros caracteres, otros caracteres no hacen nada
Accid Bright
27

Mejora menor a la solución de ujell: si usa NSURL en lugar de NSString, puede usar cualquier URL (por ejemplo, URL personalizadas)

NSURL *URL = [NSURL URLWithString: @"whatsapp://app"];
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"start Whatsapp"];
[str addAttribute: NSLinkAttributeName value:URL range: NSMakeRange(0, str.length)];
yourTextField.attributedText = str;

¡Que te diviertas!

Hans One
fuente
21

Swift 4:

var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedStringKey.link: URL(string: "http://www.google.com")!])

yourTextView.attributedText = attributedString

Swift 3.1:

var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSLinkAttributeName: URL(string: "http://www.google.com")!])

yourTextView.attributedText = attributedString
Bill Chan
fuente
Esta respuesta funciona perfectamente como está. No parece necesitar ninguno de los colores o subclases personalizados que usan otras respuestas.
zeroimpl
19

Yo también tenía un requisito similar, inicialmente usé UILabel y luego me di cuenta de que UITextView es mejor. Hice que UITextView se comportara como UILabel deshabilitando la interacción y el desplazamiento e hice un método de categoría para NSMutableAttributedStringestablecer el enlace al texto igual que lo que Karl había hecho (+1 para eso) esta es mi versión obj c

-(void)setTextAsLink:(NSString*) textToFind withLinkURL:(NSString*) url
{
    NSRange range = [self.mutableString rangeOfString:textToFind options:NSCaseInsensitiveSearch];

    if (range.location != NSNotFound) {

        [self addAttribute:NSLinkAttributeName value:url range:range];
        [self addAttribute:NSForegroundColorAttributeName value:[UIColor URLColor] range:range];
    }
}

puede usar el siguiente delegado para manejar la acción

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
    // do the task
    return YES;
}
anoop4real
fuente
1
Por lo que puedo decir, la configuración NSForegroundColorAttributeNameen un rango donde NSLinkAttributeNamese aplica no funciona. No importa cuál sea, el linkTextAttributesde la UITextViewse aplican en su lugar. ¿ NSForegroundColorAttributeNameFunciona para ti?
Dima
¿Estás seguro de que no estás configurando linkTextAttributeslo mismo? o tal vez tintColor? ¿Eres capaz de hacer que 2 enlaces aparezcan en diferentes colores en la misma vista de texto?
Dima
1
Aquí hay un código de trabajo NSRange range = [self.text rangeOfString: opciones textToFind: NSCaseInsensitiveSearch]; if (range.location! = NSNotFound) {NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString: self.text]; [string addAttribute: NSLinkAttributeName valor: url rango: rango]; [string addAttribute: NSForegroundColorAttributeName valor: [UIColor blueColor] rango: rango]; self.text = @ ""; self.attributedText = string; }
Nosov Pavel
16

Utilice UITextView, es compatible con enlaces clicables. Crea una cadena atribuida usando el siguiente código

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];

Luego configure el texto UITextView de la siguiente manera

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],

                                 NSUnderlineColorAttributeName: [UIColor blueColor],

                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;

Asegúrese de habilitar el comportamiento "Seleccionable" de UITextView en XIB.

Nitheesh George
fuente
15
¡Creo que esta es la mejor solución! ¡La nota sobre la habilitación Selectablees importante!
LunaCodeGirl
Esto no subrayó el enlace para mí (iOS 7, 8). Necesitaba usar NSUnderlineStyleAttributeName: [NSNumber numberWithInt: NSUnderlineStyleSingle]
prewett
1
¡hacerla seleccionable es la información más importante y no intuitiva!
Nicolas Massart
13

El meollo de mi pregunta era que quería poder crear enlaces en los que se pueda hacer clic en vistas de texto / campos / etiquetas sin tener que escribir un código personalizado para manipular el texto y agregar los enlaces. Quería que se basara en datos.

Finalmente descubrí cómo hacerlo. El problema es que IB no respeta los enlaces incrustados.

Además, la versión de iOS de NSAttributedStringno le permite inicializar una cadena atribuida desde un archivo RTF. La versión de OS X de NSAttributedString does tener un inicializador que toma un archivo RTF como entrada.

NSAttributedString cumple con el protocolo NSCoding, por lo que puede convertirlo a / desde NSData

Creé una herramienta de línea de comandos OS X que toma un archivo RTF como entrada y genera un archivo con la extensión .data que contiene NSData de NSCoding. Luego pongo el archivo .data en mi proyecto y agrego un par de líneas de código que cargan el texto en la vista. El código se ve así (este proyecto estaba en Swift):

/*
If we can load a file called "Dates.data" from the bundle and convert it to an attributed string,
install it in the dates field. The contents contain clickable links with custom URLS to select
each date.
*/
if
  let datesPath = NSBundle.mainBundle().pathForResource("Dates", ofType: "data"),
  let datesString = NSKeyedUnarchiver.unarchiveObjectWithFile(datesPath) as? NSAttributedString
{
  datesField.attributedText = datesString
}

Para las aplicaciones que usan mucho texto formateado, creo una regla de compilación que le dice a Xcode que todos los archivos .rtf en una carpeta dada son fuente y que los archivos .data son la salida. Una vez que hago eso, simplemente agrego archivos .rtf al directorio designado (o edito archivos existentes) y el proceso de compilación descubre que son nuevos / actualizados, ejecuta la herramienta de línea de comandos y copia los archivos en el paquete de la aplicación. Funciona muy bien

Escribí una publicación de blog que vincula a un proyecto de muestra (Swift) que demuestra la técnica. Puedes verlo aqui:

Crear URL en las que se puede hacer clic en un UITextField que se abre en su aplicación

Duncan C
fuente
11

Ejemplo de Swift 3 para detectar acciones en toques de texto atribuidos

https://stackoverflow.com/a/44226491/5516830

let termsAndConditionsURL = TERMS_CONDITIONS_URL;
let privacyURL            = PRIVACY_URL;

override func viewDidLoad() {
    super.viewDidLoad()

    self.txtView.delegate = self
    let str = "By continuing, you accept the Terms of use and Privacy policy"
    let attributedString = NSMutableAttributedString(string: str)
    var foundRange = attributedString.mutableString.range(of: "Terms of use") //mention the parts of the attributed text you want to tap and get an custom action
    attributedString.addAttribute(NSLinkAttributeName, value: termsAndConditionsURL, range: foundRange)
    foundRange = attributedString.mutableString.range(of: "Privacy policy")
    attributedString.addAttribute(NSLinkAttributeName, value: privacyURL, range: foundRange)
    txtView.attributedText = attributedString
}

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewController(withIdentifier: "WebView") as! SKWebViewController

    if (URL.absoluteString == termsAndConditionsURL) {
        vc.strWebURL = TERMS_CONDITIONS_URL
        self.navigationController?.pushViewController(vc, animated: true)
    } else if (URL.absoluteString == privacyURL) {
        vc.strWebURL = PRIVACY_URL
        self.navigationController?.pushViewController(vc, animated: true)
    }
    return false
}

Como sabio, puede agregar cualquier acción que desee con shouldInteractWith URL método UITextFieldDelegate.

¡¡Salud!!

Akila Wasala
fuente
7

La respuesta rápida es usar UITextView en lugar de UILabel. Necesita habilitar Selectabley deshabilitarEditable .

Luego deshabilite los indicadores de desplazamiento y los rebotes.

Captura de pantalla

Captura de pantalla

Mi solución usando una NSMutableAttributedStringcadena htmlNSHTMLTextDocumentType

NSString *s = @"<p><a href='https://itunes.apple.com/us/app/xxxx/xxxx?mt=8'>https://itunes.apple.com/us/app/xxxx/xxxx?mt=8</a></p>";

NSMutableAttributedString *text = [[NSMutableAttributedString alloc]
                                           initWithData: [s dataUsingEncoding:NSUnicodeStringEncoding]
                                           options: @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
                                           documentAttributes: nil
                                           error: nil
                                           ];

cell.content.attributedText = text;
Dody Rachmat Wicaksono
fuente
Esta. ¡Pude leer un archivo RTF de mi paquete de recursos, convertirlo NSAttributedString, configurarlo como attributedTextmi UITextViewy los hipervínculos simplemente funcionan! Hubiera sido mucho trabajo encontrar el rango de cada hipervínculo y configurarlo usando atributos.
Nicolas Miari
6

He escrito un método que agrega un enlace (linkString) a una cadena (fullString) con una cierta url (urlString):

- (NSAttributedString *)linkedStringFromFullString:(NSString *)fullString withLinkString:(NSString *)linkString andUrlString:(NSString *)urlString
{
    NSRange range = [fullString rangeOfString:linkString options:NSLiteralSearch];
    NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:fullString];

    NSMutableParagraphStyle *paragraphStyle = NSMutableParagraphStyle.new;
    paragraphStyle.alignment = NSTextAlignmentCenter;
    NSDictionary *attributes = @{NSForegroundColorAttributeName:RGB(0x999999),
                                 NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:10],
                                 NSParagraphStyleAttributeName:paragraphStyle};
    [str addAttributes:attributes range:NSMakeRange(0, [str length])];
    [str addAttribute: NSLinkAttributeName value:urlString range:range];

    return str;
}

Deberías llamarlo así:

NSString *fullString = @"A man who bought the Google.com domain name for $12 and owned it for about a minute has been rewarded by Google for uncovering the flaw.";
NSString *linkString = @"Google.com";
NSString *urlString = @"http://www.google.com";

_youTextView.attributedText = [self linkedStringFromFullString:fullString withLinkString:linkString andUrlString:urlString];
Denis Kutlubaev
fuente
Se puede hacer clic pero no abre el enlace ni nada. simplemente hace clic como un botón que no hace nada.
Reza.Ab
5

Necesitaba seguir usando un UILabel puro, así lo llamé desde mi reconocedor de tomas (esto se basa en la respuesta de malex aquí: índice de caracteres en el punto de contacto para UILabel )

UILabel* label = (UILabel*)gesture.view;
CGPoint tapLocation = [gesture locationInView:label];

// create attributed string with paragraph style from label

NSMutableAttributedString* attr = [label.attributedText mutableCopy];
NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.alignment = label.textAlignment;

[attr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, label.attributedText.length)];

// init text storage

NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attr];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];

// init text container

NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height+100) ];
textContainer.lineFragmentPadding  = 0;
textContainer.maximumNumberOfLines = label.numberOfLines;
textContainer.lineBreakMode        = label.lineBreakMode;

[layoutManager addTextContainer:textContainer];

// find tapped character

NSUInteger characterIndex = [layoutManager characterIndexForPoint:tapLocation
                                                  inTextContainer:textContainer
                         fractionOfDistanceBetweenInsertionPoints:NULL];

// process link at tapped character

[attr enumerateAttributesInRange:NSMakeRange(characterIndex, 1)
                                         options:0
                                      usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {
                                          if (attrs[NSLinkAttributeName]) {
                                              NSString* urlString = attrs[NSLinkAttributeName];
                                              NSURL* url = [NSURL URLWithString:urlString];
                                              [[UIApplication sharedApplication] openURL:url];
                                          }
                                      }];
masty
fuente
Esto fue bastante útil, no pude obtener índices de los caracteres en la última línea. Su código tiene el +100 en el textContainer cuando inicia el CGSize, lo que no tiene mucho sentido para mí, pero funcionó.
blueether
4

Actualizar:

Había 2 partes clave en mi pregunta:

  1. Cómo hacer un enlace donde el texto que se muestra para el enlace en el que se puede hacer clic es diferente del enlace real que se invoca:
  2. Cómo configurar los enlaces sin tener que usar un código personalizado para establecer los atributos en el texto.

Resulta que iOS 7 agregó la capacidad de cargar texto atribuido desde NSData .

Creé una subclase personalizada UITextViewque aprovecha la@IBInspectable atributo y le permite cargar contenido de un archivo RTF directamente en IB. Simplemente escriba el nombre del archivo en IB y la clase personalizada hace el resto.

Aquí están los detalles:

En iOS 7, NSAttributedStringganó el método initWithData:options:documentAttributes:error:. Ese método le permite cargar una NSAttributedString desde un objeto NSData. Primero puede cargar un archivo RTF en NSData, luego usarlo initWithData:options:documentAttributes:error:para cargar ese NSData en su vista de texto. (Tenga en cuenta que también hay un método initWithFileURL:options:documentAttributes:error:que cargará una cadena atribuida directamente desde un archivo, pero ese método se desaprobó en iOS 9. Es más seguro usar el método initWithData:options:documentAttributes:error:, que no se desaprobó.

Quería un método que me permitiera instalar enlaces clicables en mis vistas de texto sin tener que crear ningún código específico para los enlaces que estaba usando.

La solución que se me ocurrió fue crear una subclase personalizada de UITextView a la que llamo RTF_UITextViewy asignarle una @IBInspectablepropiedad llamada RTF_Filename. Agregar el @IBInspectableatributo a una propiedad hace que Interface Builder exponga esa propiedad en el "Inspector de atributos". Luego puede establecer ese valor desde IB sin código personalizado.

También agregué un @IBDesignableatributo a mi clase personalizada. El @IBDesignableatributo le dice a Xcode que debe instalar una copia en ejecución de su clase de vista personalizada en el generador de interfaces para que pueda verla en la visualización gráfica de su jerarquía de vistas. () Desafortunadamente, para esta clase, la @IBDesignablepropiedad parece ser escamosa. Funcionó cuando lo agregué por primera vez, pero luego eliminé el contenido de texto sin formato de mi vista de texto y los enlaces en los que se puede hacer clic desaparecieron y no he podido recuperarlos).

El código para mi RTF_UITextViewes muy simple. Además de agregar el @IBDesignableatributo y una RTF_Filenamepropiedad con el @IBInspectableatributo, agregué un didSet()método a la RTF_Filenamepropiedad. El didSet()método se llama cada vez que RTF_Filenamecambia el valor de la propiedad. El código para el didSet()método es bastante simple:

@IBDesignable
class RTF_UITextView: UITextView
{
  @IBInspectable
  var RTF_Filename: String?
    {
    didSet(newValue)
    {
      //If the RTF_Filename is nil or the empty string, don't do anything
      if ((RTF_Filename ?? "").isEmpty)
      {
        return
      }
      //Use optional binding to try to get an URL to the
      //specified filename in the app bundle. If that succeeds, try to load
      //NSData from the file.
      if let fileURL = NSBundle.mainBundle().URLForResource(RTF_Filename, withExtension: "rtf"),
        
        //If the fileURL loads, also try to load NSData from the URL.
        let theData = NSData(contentsOfURL: fileURL)
      {
        var aString:NSAttributedString
        do
        {
          //Try to load an NSAttributedString from the data
          try
            aString = NSAttributedString(data: theData,
              options: [:],
              documentAttributes:  nil
          )
          //If it succeeds, install the attributed string into the field.
          self.attributedText = aString;
        }
        catch
        {
          print("Nerp.");
        }
      }
      
    }
  }
}

Tenga en cuenta que si la propiedad @IBDesignable no le permitirá obtener una vista previa de su texto con estilo en el generador de interfaces, entonces sería mejor configurar el código anterior como una extensión de UITextView en lugar de una subclase personalizada. De esa manera, podría usarlo en cualquier vista de texto sin tener que cambiar la vista de texto a la clase personalizada.

Vea mi otra respuesta si necesita admitir versiones de iOS anteriores a iOS 7.

Puede descargar un proyecto de muestra que incluye esta nueva clase desde gitHub:

Proyecto de demostración DatesInSwift en Github

Duncan C
fuente
3

Simplemente encuentre una solución sin código para UITextView: ingrese la descripción de la imagen aquí

¡Activar Detección-> Opciones de enlaces, la URL y también el correo electrónico serán detectados y se podrá hacer clic en ellos!

Bill Chan
fuente
3
Eso hace que se pueda hacer clic en los enlaces. Quiero tener texto legible por el usuario que tenga un enlace detrás. Vea el ejemplo en mi pregunta original.
Duncan C
Sí, mi respuesta solo se aplica al caso de que el enlace sea el mismo que el texto. Si el enlace es otra cosa, seguiría la respuesta de @ ujell.
Bill Chan
3
Mi pregunta era muy específicamente sobre el texto en el que se puede hacer clic y que muestra algo distinto de la URL. No hiciste más que echar un vistazo a la pregunta, ¿verdad?
Duncan C
1
no sirvió para otros propósitos, pero seguramente esto es lo que vine a apilar buscando ... una forma de hacer clic en los enlaces de mi aplicación de chat. Bingo Encontré este artículo ... ¡gracias! Wish xcode permitiría habilitar twitter y hash tag.
MizAkita
Esto funciona incluso con texto personalizado en lugar de enlace sin formato. Recuerde seleccionar Comportamiento -> Seleccionable y Detección -> Enlaces.
krlbsk
3

Versión rápida:

    // Attributed String for Label
    let plainText = "Apkia"
    let styledText = NSMutableAttributedString(string: plainText)
    // Set Attribuets for Color, HyperLink and Font Size
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(14.0), NSLinkAttributeName:NSURL(string: "http://apkia.com/")!, NSForegroundColorAttributeName: UIColor.blueColor()]
    styledText.setAttributes(attributes, range: NSMakeRange(0, plainText.characters.count))
    registerLabel.attributedText = styledText
ioopl
fuente
3

Use UITextView y establezca dataDetectorTypes para Link.

Me gusta esto:

testTextView.editable = false 
testTextView.dataDetectorTypes = .link

Si desea detectar el enlace, el número de teléfono, la dirección, etc., entonces

testTextView.dataDetectorTypes = .all
Adarsh ​​GJ
fuente
3
No. Esto solo te permite hacer clic en los enlaces. Mi pregunta es específica para hacer que se pueda hacer clic en texto arbitrario como "haga clic aquí", no en una URL comohttp://somedomain/someurl?param=value
Duncan C
2

Una adición rápida a la descripción original de Duncan C frente al comportamiento de IB. Él escribe: "Es trivial hacer que se pueda hacer clic en los hipervínculos en un UITextView. Simplemente configura la casilla de verificación" detectar enlaces "en la vista en IB, y detecta enlaces http y los convierte en hipervínculos".

Mi experiencia (al menos en xcode 7) es que también debe desmarcar el comportamiento "Editable" para que las URL se detecten y se pueda hacer clic en ellas.

sakumatto
fuente
2

En caso de que tenga problemas con lo que @Karl Nosworthy y @esilver habían proporcionado anteriormente, he actualizado la extensión NSMutableAttributedString a su versión Swift 4.

extension NSMutableAttributedString {

public func setAsLink(textToFind:String, linkURL:String) -> Bool {

    let foundRange = self.mutableString.range(of: textToFind)
    if foundRange.location != NSNotFound {
         _ = NSMutableAttributedString(string: textToFind)
        // Set Attribuets for Color, HyperLink and Font Size
        let attributes = [NSFontAttributeName: UIFont.bodyFont(.regular, shouldResize: true), NSLinkAttributeName:NSURL(string: linkURL)!, NSForegroundColorAttributeName: UIColor.blue]

        self.setAttributes(attributes, range: foundRange)
        return true
    }
    return false
  }
}
Vick Swift
fuente
0

Si desea usar NSLinkAttributeName en un UITextView, entonces puede considerar usar la biblioteca AttributedTextView. Es una subclase de UITextView que hace que sea muy fácil manejarlos. Para obtener más información, consulte: https://github.com/evermeer/AttributedTextView

Puede hacer que cualquier parte del texto interactúe de esta manera (donde textView1 es un UITextView IBoutlet):

textView1.attributer =
    "1. ".red
    .append("This is the first test. ").green
    .append("Click on ").black
    .append("evict.nl").makeInteract { _ in
        UIApplication.shared.open(URL(string: "http://evict.nl")!, options: [:], completionHandler: { completed in })
    }.underline
    .append(" for testing links. ").black
    .append("Next test").underline.makeInteract { _ in
        print("NEXT")
    }
    .all.font(UIFont(name: "SourceSansPro-Regular", size: 16))
    .setLinkColor(UIColor.purple) 

Y para manejar hashtags y menciones puedes usar un código como este:

textView1.attributer = "@test: What #hashtags do we have in @evermeer #AtributedTextView library"
    .matchHashtags.underline
    .matchMentions
    .makeInteract { link in
        UIApplication.shared.open(URL(string: "https://twitter.com\(link.replacingOccurrences(of: "@", with: ""))")!, options: [:], completionHandler: { completed in })
    }
Edwin Vermeer
fuente
0
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],   
                                 NSUnderlineColorAttributeName: [UIColor blueColor],
                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;

PUNTOS CLAVE:

  • Asegúrese de habilitar el comportamiento "Seleccionable" de UITextView en XIB.
  • Asegúrese de deshabilitar el comportamiento "Editable" de UITextView en XIB.
Shashank Sharma
fuente