Comment puis-je créer un lien cliquable dans une chaîne NSAttributedString?

200

Il est trivial de rendre les hyperliens cliquables dans un fichier UITextView. Vous venez de définir la case à cocher "détecter les liens" sur la vue dans IB, et il détecte les liens HTTP et les transforme en hyperliens.

Cependant, cela signifie toujours que ce que l'utilisateur voit est le lien "brut". Les fichiers RTF et HTML vous permettent tous les deux de configurer une chaîne lisible par l'utilisateur avec un lien "derrière".

Il est facile d'installer du texte attribué dans une vue de texte (ou un UILabelou UITextField, d'ailleurs.) Cependant, lorsque ce texte attribué comprend un lien, il n'est pas cliquable.

Existe-t-il un moyen de rendre le texte lisible par l'utilisateur cliquable dans un UITextView, UILabelou UITextField?

Le balisage est différent sur SO, mais voici l'idée générale. Ce que je veux, c'est un texte comme celui-ci:

Ce morph a été généré avec Face Dancer , Cliquez pour voir dans l'App Store.

La seule chose que je puisse obtenir est la suivante:

Cette évolution a été générée avec Face Dancer, cliquez sur http://example.com/facedancer pour l'afficher dans l'App Store.

Duncan C
la source
Essayez cet échantillon .. IFTweetLabel J'espère que cela aide ..
Vidhyanand
7
Copie
Senseful
Bon travail passant 100K en un clin d'œil. Bienvenue au club 100K. Bien mérité!
vacawama
@vacawama, attendez, quand est-ce arrivé? J'étais à ≈98k la dernière fois que j'ai regardé! (J'entends des rumeurs selon lesquelles vous avez du SO swag en tant que membre du club 100k?)
Duncan C
Ils ont changé les votes positifs sur les questions de +5 à +10, donc si vous aviez 800 votes positifs, vous obtiendrez +4000 en un éclair. J'attends toujours 100k swag (croisé en avril). Quelque chose au sujet du changement de fournisseurs de swag ...
vacawama

Réponses:

156

Utilisez NSMutableAttributedString .

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

Modifier :

Il ne s'agit pas directement de la question mais simplement pour clarifier UITextFieldet UILabelne prend pas en charge l'ouverture d'URL. Si vous souhaitez utiliser UILabeldes liens, vous pouvez vérifier TTTAttributedLabel .

Aussi , vous devez définir la dataDetectorTypesvaleur de votre UITextViewà UIDataDetectorTypeLinkou UIDataDetectorTypeAllà ouvrir des URL lorsque vous cliquez dessus. Ou vous pouvez utiliser la méthode déléguée comme suggéré dans les commentaires.

ujell
la source
7
Oui, cela fonctionne, il suffit de le placer dans une méthode déléguée UITextView et de remplacer: - (BOOL) textView: (UITextView *) textView shouldInteractWithURL: (NSURL *) url inRange: (NSRange) characterRange
Yunus Nedim Mehel
Cela ne fonctionne pas dans un UILabel - rien ne se produit lorsque vous appuyez sur le champ.
Jack BeNimble
7
@saboehnke voulez-vous dire appeler une méthode lorsque le lien est cliqué? si c'est le cas, implémentez la méthode déléguée, donnez une URL factice comme attribut et appelez votre méthode dans- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
ujell
2
Je ne sais pas comment ça marche. La valeur de l'attribut doit être de type 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

J'ai trouvé cela vraiment utile, mais je devais le faire à plusieurs endroits, j'ai donc enveloppé mon approche dans une simple extension pour 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
   }
}

Exemple d'utilisation:

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

if linkWasSet {
    // adjust more attributedString properties
}

Objectif c

Je viens de rencontrer une exigence de faire de même dans un projet Objective-C pur, alors voici la catégorie 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

Exemple d'utilisation:

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

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

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

Assurez-vous que l'attribut Comportement de NSTextField est défini comme sélectionnable. Attribut de comportement Xcode NSTextField

Karl Nosworthy
la source
Un exemple rapide d'utilisation / implémentation de ceci serait grandement apprécié.
ioopl
3
@ioop. J'ai ajouté un très petit exemple au message d'origine ci-dessus, j'espère que cela vous aidera.
Karl Nosworthy
7
Cela a fonctionné correctement. Je veux juste dire que vous devez rendre votre UITextView sélectionnable pour permettre au lien d'être cliquable
lujop
1
@felecia genet, dans les implémentations Objective C et Swift, la méthode renvoie un résultat booléen pour indiquer si une correspondance et un ensemble résultant ont eu lieu. L'erreur que vous voyez est parce que vous ne capturez pas ce résultat - ce qui est bien. Vous pouvez soit capturer ce résultat en l'affectant à une variable locale, soit ajuster la méthode pour l'empêcher de renvoyer la valeur booléenne si cela correspond mieux à vos besoins. J'espère que ça aide?
Karl Nosworthy
1
Pas de problème @feleciagenet, j'ai ajouté le stockage et la vérification du résultat de la méthode aux exemples Swift et ObjectiveC.
Karl Nosworthy
34

Je viens de créer une sous-classe d'UILabel pour répondre spécifiquement à ces cas d'utilisation. Vous pouvez facilement ajouter plusieurs liens et définir pour eux différents gestionnaires. Il prend également en charge la mise en surbrillance du lien enfoncé lorsque vous touchez pour un retour tactile. Veuillez vous référer à https://github.com/null09264/FRHyperLabel .

Dans votre cas, le code peut ressembler à ceci:

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

Exemple de capture d'écran (le gestionnaire est configuré pour déclencher une alerte au lieu d'ouvrir une URL dans ce cas)

faceancer

Jinghan Wang
la source
si je suppose que mon texte est comme ceci Ce morphing a été généré avec Face Dancer, cliquez sur la vue Face Dancer dans l'App Store Face Dancer. ici, j'ai 3 Face Dancer, cela ne fonctionnait pas pour lui
MANCHIKANTI KRISHNAKISHORE
1
Dans ce cas, veuillez utiliser l'API à la - (void)setLinkForRange:(NSRange)range withLinkHandler:(void(^)(FRHyperLabel *label, NSRange selectedRange))handler; place. Veuillez vous référer au fichier Lisezmoi de la page github.
Jinghan Wang
1
FRHyperLabel ne semble plus fonctionner. Dans "characterIndexForPoint:", il renvoie toujours -1 (introuvable).
John Pang
Ne fonctionne pas pour moi pour une étiquette multiligne. La détection des caractères est incorrecte. La chaîne de lien de 15 caractères n'est cliquable que sur certains premiers caractères, d'autres caractères ne font rien
Accid Bright
27

Amélioration mineure de la solution d'ujell: si vous utilisez NSURL au lieu d'une chaîne NSString, vous pouvez utiliser n'importe quelle URL (par exemple des URL personnalisées)

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;

S'amuser!

Hans One
la source
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
la source
Cette réponse fonctionne parfaitement telle quelle. Ne semble pas avoir besoin des sous-classes de coloration ou personnalisées que les autres réponses utilisent.
zeroimpl
19

Moi aussi, j'avais une exigence similaire, j'ai d'abord utilisé UILabel, puis j'ai réalisé que UITextView était mieux. J'ai fait UITextView se comporter comme UILabel en désactivant l'interaction et le défilement et j'ai créé une méthode de catégorie pour NSMutableAttributedStringdéfinir le lien vers le texte comme ce que Karl avait fait (+1 pour cela) c'est ma version 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];
    }
}

vous pouvez ensuite utiliser le délégué ci-dessous pour gérer l'action

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
    // do the task
    return YES;
}
anoop4real
la source
1
Autant que je sache, le réglage NSForegroundColorAttributeNamedans une plage où NSLinkAttributeNameest appliqué ne fonctionne pas. Quoi qu'il en soit, les linkTextAttributesdu UITextViewsont appliqués à la place. Ça NSForegroundColorAttributeNamemarche pour vous?
Dima
Êtes-vous sûr que vous ne définissez linkTextAttributespas la même chose? ou peut tintColor- être ? Pouvez-vous faire apparaître 2 liens de différentes couleurs dans la même vue de texte?
Dima
1
Voici un code de travail NSRange range = [self.text rangeOfString: options textToFind: NSCaseInsensitiveSearch]; if (range.location! = NSNotFound) {NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString: self.text]; [chaîne addAttribute: valeur NSLinkAttributeName: plage URL: plage]; [chaîne addAttribute: NSForegroundColorAttributeName valeur: [UIColor blueColor] plage: plage]; self.text = @ ""; self.attributedText = string; }
Nosov Pavel
16

Utilisez UITextView, il prend en charge les liens cliquables. Créez une chaîne attribuée en utilisant le code suivant

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

Ensuite, définissez le texte UITextView comme suit

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

                                 NSUnderlineColorAttributeName: [UIColor blueColor],

                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

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

Assurez-vous que vous activez le comportement "sélectionnable" de l'UITextView dans XIB.

Nitheesh George
la source
15
Je pense que c'est la meilleure solution! La note sur l'activation Selectableest importante!
LunaCodeGirl
Cela n'a pas souligné le lien pour moi (iOS 7, 8). J'avais besoin d'utiliser NSUnderlineStyleAttributeName: [NSNumber numberWithInt: NSUnderlineStyleSingle]
prewett
1
le rendre sélectionnable est l'information la plus importante et non intuitive!
Nicolas Massart
13

Le cœur de ma question était que je voulais pouvoir créer des liens cliquables dans les vues / champs / étiquettes de texte sans avoir à écrire du code personnalisé pour manipuler le texte et ajouter les liens. Je voulais que ce soit basé sur les données.

J'ai finalement compris comment le faire. Le problème est que IB n'honore pas les liens intégrés.

De plus, la version iOS de NSAttributedStringne vous permet pas d'initialiser une chaîne attribuée à partir d'un fichier RTF. La version OS X de NSAttributedString possède un initialiseur qui prend un fichier RTF en entrée.

NSAttributedString est conforme au protocole NSCoding, vous pouvez donc le convertir vers / depuis NSData

J'ai créé un outil de ligne de commande OS X qui prend un fichier RTF en entrée et génère un fichier avec l'extension .data qui contient le NSData de NSCoding. J'ai ensuite mis le fichier .data dans mon projet et ajouté quelques lignes de code qui chargent le texte dans la vue. Le code ressemble à ceci (ce projet était 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
}

Pour les applications qui utilisent beaucoup de texte formaté, je crée une règle de construction qui indique à Xcode que tous les fichiers .rtf d'un dossier donné sont source et que les fichiers .data sont la sortie. Une fois que je fais cela, j'ajoute simplement des fichiers .rtf au répertoire désigné (ou modifie les fichiers existants) et le processus de construction détermine qu'ils sont nouveaux / mis à jour, exécute l'outil de ligne de commande et copie les fichiers dans le bundle d'application. Cela fonctionne à merveille.

J'ai écrit un article de blog qui renvoie à un exemple de projet (Swift) démontrant la technique. Tu peux le voir ici:

Création d'URL cliquables dans un UITextField qui s'ouvrent dans votre application

Duncan C
la source
11

Exemple Swift 3 pour détecter les actions sur les tapotements de texte attribués

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
}

Comme sage, vous pouvez ajouter n'importe quelle action que vous souhaitez avec la shouldInteractWith URLméthode UITextFieldDelegate.

À votre santé!!

Akila Wasala
la source
7

La réponse rapide utilise UITextView au lieu de UILabel. Vous devez activer Selectableet désactiver Editable.

Désactivez ensuite les indicateurs de défilement et les rebonds.

Capture d'écran

Capture d'écran

Ma solution en utilisant une NSMutableAttributedStringchaîne 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
la source
Ce. J'ai pu lire un fichier RTF à partir de mon ensemble de ressources, le convertir en NSAttributedString, le définir comme le attributedTextmon UITextViewet les hyperliens fonctionnent! Il aurait fallu beaucoup de travail pour trouver la plage de chaque hyperlien et le configurer à l'aide d'attributs.
Nicolas Miari
6

J'ai écrit une méthode, qui ajoute un lien (linkString) à une chaîne (fullString) avec une certaine 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;
}

Vous devriez l'appeler comme ceci:

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
la source
Il est cliquable mais il n'ouvre pas le lien ou quoi que ce soit. il clique simplement comme un bouton qui ne fait rien.
Reza,
5

Je devais continuer à utiliser un UILabel pur, ainsi appelé à partir de mon identificateur de prise (cela est basé sur la réponse de malex ici: Index des caractères au point de contact pour 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
la source
C'était très utile, je n'ai pas pu obtenir les index des caractères sur la dernière ligne. Votre code a le +100 sur le textContainer lors du lancement de la CGSize, ce qui n'a pas beaucoup de sens pour moi, mais il a fait l'affaire.
blueether
4

Mettre à jour:

Ma question comportait 2 éléments clés:

  1. Comment créer un lien où le texte affiché pour le lien cliquable est différent du lien réel qui est invoqué:
  2. Comment configurer les liens sans avoir à utiliser de code personnalisé pour définir les attributs sur le texte.

Il s'avère qu'iOS 7 a ajouté la possibilité de charger le texte attribué à partir de NSData.

J'ai créé une sous-classe personnalisée UITextViewqui tire parti de l' @IBInspectableattribut et vous permet de charger le contenu d'un fichier RTF directement dans IB. Vous tapez simplement le nom de fichier dans IB et la classe personnalisée fait le reste.

Voici les détails:

Dans iOS 7, a NSAttributedStringgagné la méthode initWithData:options:documentAttributes:error:. Cette méthode vous permet de charger un NSAttributedString à partir d'un objet NSData. Vous pouvez d'abord charger un fichier RTF dans NSData, puis l'utiliser initWithData:options:documentAttributes:error:pour charger ce NSData dans votre vue texte. (Notez qu'il existe également une méthode initWithFileURL:options:documentAttributes:error:qui chargera une chaîne attribuée directement à partir d'un fichier, mais cette méthode a été déconseillée dans iOS 9. Il est plus sûr d'utiliser la méthode initWithData:options:documentAttributes:error:, qui n'était pas déconseillée.

Je voulais une méthode qui me permette d'installer des liens cliquables dans mes vues de texte sans avoir à créer de code spécifique aux liens que j'utilisais.

La solution que j'ai trouvée était de créer une sous-classe personnalisée de UITextView que j'appelle RTF_UITextViewet de lui donner une @IBInspectablepropriété appelée RTF_Filename. L'ajout de l' @IBInspectableattribut à une propriété oblige Interface Builder à exposer cette propriété dans «l'inspecteur d'attributs». Vous pouvez ensuite définir cette valeur à partir d'IB sans code personnalisé.

J'ai également ajouté un @IBDesignableattribut à ma classe personnalisée. L' @IBDesignableattribut indique à Xcode qu'il doit installer une copie en cours d'exécution de votre classe de vue personnalisée dans le générateur d'interface afin que vous puissiez la voir dans l'affichage graphique de votre hiérarchie de vues. () Malheureusement, pour cette classe, la @IBDesignablepropriété semble floconneuse. Cela a fonctionné lorsque je l'ai ajouté pour la première fois, mais j'ai ensuite supprimé le contenu en texte brut de mon affichage de texte et les liens cliquables de mon affichage ont disparu et je n'ai pas pu les récupérer.)

Le code pour my RTF_UITextViewest très simple. En plus d'ajouter l' @IBDesignableattribut et une RTF_Filenamepropriété avec l' @IBInspectableattribut, j'ai ajouté une didSet()méthode à la RTF_Filenamepropriété. La didSet()méthode est appelée à chaque fois que la valeur de la RTF_Filenamepropriété change. Le code de la didSet()méthode est assez 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.");
        }
      }
      
    }
  }
}

Notez que si la propriété @IBDesignable ne vous permet pas de prévisualiser de manière fiable votre texte stylisé dans le générateur d'interface, il peut être préférable de définir le code ci-dessus comme une extension de UITextView plutôt qu'une sous-classe personnalisée. De cette façon, vous pouvez l'utiliser dans n'importe quelle vue de texte sans avoir à changer la vue de texte en classe personnalisée.

Voir mon autre réponse si vous devez prendre en charge les versions iOS avant iOS 7.

Vous pouvez télécharger un exemple de projet qui inclut cette nouvelle classe depuis gitHub:

Projet de démonstration DatesInSwift sur Github

Duncan C
la source
3

Trouvez simplement une solution sans code pour UITextView: entrez la description de l'image ici

Activez les options Détection-> Liens, l'URL et également le courrier électronique seront détectés et cliquables!

Bill Chan
la source
3
Cela rend les liens cliquables. Je veux avoir un texte lisible par l'utilisateur avec un lien derrière. Voir l'exemple dans ma question d'origine.
Duncan C
Oui, ma réponse ne s'applique qu'au cas où le lien est le même que le texte. Si le lien est autre chose, je suivrais la réponse de @ ujell.
Bill Chan
3
Ma question portait très précisément sur le texte cliquable qui affiche autre chose que l'URL. Vous n'avez fait que jeter un coup d'œil à la question, n'est-ce pas?
Duncan C
1
n'a pas servi à d'autres, mais c'est sûrement ce que je suis venu chercher pour trouver un moyen de rendre les liens dans mon application de chat cliquables. Bingo J'ai trouvé cet article ... merci! Souhaiter que xcode permette l'activation de Twitter et des balises de hachage.
MizAkita
Cela fonctionne même avec du texte personnalisé inséré dans un lien brut. N'oubliez pas de sélectionner Comportement -> Sélectionnable et détection -> Liens.
krlbsk
3

Version rapide:

    // 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
la source
3

Utilisez UITextView et définissez dataDetectorTypes pour Link.

comme ça:

testTextView.editable = false 
testTextView.dataDetectorTypes = .link

Si vous souhaitez détecter un lien, un numéro de téléphone, une adresse, etc., puis

testTextView.dataDetectorTypes = .all
Adarsh ​​GJ
la source
3
Non. Cela vous permet uniquement de rendre les liens cliquables. Ma question est spécifique à rendre le texte arbitraire comme "cliquez ici" cliquable, pas une URL commehttp://somedomain/someurl?param=value
Duncan C
2

Un ajout rapide à la description originale de Duncan C vis-à-vis du comportement de l'IB. Il écrit: "Il est trivial de rendre les hyperliens cliquables dans un UITextView. Vous venez de définir la case" détecter les liens "sur la vue dans IB, et il détecte les liens http et les transforme en hyperliens."

Mon expérience (au moins dans xcode 7) est que vous devez également décocher le comportement "Modifiable" pour que les URL soient détectées et cliquables.

Sakumatto
la source
2

Si vous rencontrez des problèmes avec ce que @Karl Nosworthy et @esilver ont fourni ci-dessus, j'ai mis à jour l'extension NSMutableAttributedString vers sa version 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
la source
0

Si vous souhaitez utiliser NSLinkAttributeName dans un UITextView, vous pouvez envisager d'utiliser la bibliothèque AttributedTextView. Il s'agit d'une sous-classe UITextView qui facilite leur gestion. Pour plus d'informations, voir: https://github.com/evermeer/AttributedTextView

Vous pouvez faire en sorte que n'importe quelle partie du texte interagisse comme ceci (où textView1 est un IBoutlet UITextView):

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) 

Et pour gérer les hashtags et les mentions, vous pouvez utiliser du code comme celui-ci:

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
la source
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;

POINTS CLÉS:

  • Assurez-vous que vous activez le comportement "sélectionnable" de l'UITextView dans XIB.
  • Assurez-vous que vous désactivez le comportement "modifiable" de l'UITextView dans XIB.
Shashank Sharma
la source