Comment puis-je faire un lien cliquable dans une chaîne Nsattribute?
c'est trivial de rendre les hyperliens cliquables dans un UITextView
. Vous venez de placer la case à cocher" detect links " sur la vue en 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 de configurer une chaîne de caractères lisible par l'utilisateur avec un lien "derrière".
il est facile d'installer du texte attribué dans une vue texte (ou un UILabel
ou UITextField
, d'ailleurs.) Cependant, lorsque l'attribuée texte inclut un lien, il n'est pas cliquable.
y a-t-il un moyen de rendre le texte lisible par l'utilisateur cliquable dans un UITextView
, UILabel
ou UITextField
?
le markup est différent sur SO, mais voici l'idée générale. Ce que je veux, c'est un texte comme celui-ci:
cette page a été générée par Face Dancer , Cliquez pour voir la suite stocker.
la seule chose que je peux obtenir est ceci:
cette phrase a été générée avec Face Dancer, cliquez sur http://example.com/facedancer à voir dans l'app store.
20 réponses
Use NSMutableAttributedString .
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"];
[str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)];
yourTextView.attributedText = str;
Modifier :
ce n'est pas directement sur la question, mais juste pour clarifier, UITextField
et UILabel
ne supporte pas L'ouverture des URLs. Si vous voulez utiliser UILabel
avec des liens, vous pouvez cocher Ttttattributedlabel .
vous devez aussi définir dataDetectorTypes
valeur de votre UITextView
à UIDataDetectorTypeLink
ou UIDataDetectorTypeAll
pour ouvrir les URLs lorsque cliqué. Ou vous pouvez utiliser la méthode du délégué comme suggéré dans les commentaires.
j'ai trouvé cela vraiment utile, mais je devais le faire un peu partout donc j'ai enveloppé mon approche dans une simple extension 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'usage:
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 frapper une exigence pour faire le il en va de même pour un projet purement Objectif-C, alors voici la catégorie Objectif-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'usage:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"];
BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"];
if (linkWasSet) {
// adjust more attributedString properties
}
amélioration mineure à la solution d'ujell: si vous utilisez NSURL au lieu d'un 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;
amusez-vous bien!
je viens de créer une sous-classe D'UILabel pour traiter spécialement ces cas d'utilisation. Vous pouvez ajouter des liens multiples facilement et définir différents gestionnaires pour eux. Il soutient également la mise en évidence du lien appuyé quand vous touchez vers le bas pour la rétroaction tactile. S'il vous plaît se 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 défini pour afficher une alerte au lieu d'ouvrir une url dans ce cas)
moi aussi j'avais une exigence similaire, d'abord J'ai utilisé UILabel et puis j'ai réalisé que UITextView est mieux. J'ai fait UITextView se comporter comme UILabel en désactivant l'interaction et le défilement et j'ai fait une méthode de catégorie pour NSMutableAttributedString
pour mettre le lien vers le texte comme ce que Karl avait fait (+1 pour cela) ceci 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 utiliser le délégué ci-dessous puis de gérer l'action
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
// do the task
return YES;
}
utilisez UITextView il supporte les liens cliquables. Créer une chaîne de caractères attribuée en utilisant le code suivant
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];
puis définissez UITextView text 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" Selectable " de L'UITextView dans XIB.
le cœur de ma question était que je voulais pouvoir créer des liens cliquables dans les vues de texte/champs/étiquettes sans avoir à écrire du code personnalisé pour manipuler le texte et ajouter les liens. Je voulais que ce soit basé sur des données.
j'ai enfin trouvé comment faire. Le problème est que L'IB n'honore pas les liens intégrés.
de plus, la version iOS de NSAttributedString
ne vous permet pas d'initialiser une chaîne attribuée à partir d'un fichier RTF. L'OS X version de NSAttributedString
does ont un initialiseur qui prend un fichier RTF comme entrée.
NSAttributedString
est conforme au protocole NSCoding, de sorte que vous pouvez le convertir en / à partir de NSData
j'ai créé un outil en ligne de commande OS X qui prend un fichier RTF comme entrée et produit un fichier avec l'extension .les données qui contiennent les données du NSCoding. J'ai alors mis la .fichier de données dans mon projet et d'ajouter quelques lignes de code qui charge le texte dans la vue. Le code ressemble à ceci (ce projet était dans 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 compilation qui dit à Xcode que tout le .les fichiers rtf dans un dossier source et le .les fichiers de données de sortie. Une fois que je l'ai fait, j'ajoute simplement .les fichiers rtf vers le répertoire désigné, (ou éditer les fichiers existants) et le processus de construction se rend compte qu'ils sont nouveaux/mis à jour, exécute l'outil en ligne de commande, et copie les fichiers dans le app bundle. Il fonctionne à merveille.
j'ai écrit un billet de blog qui fait le lien vers un échantillon de projet (Swift) démontrant la technique. Vous pouvez le voir ici:
créer des URLs cliquables dans un UITextField qui s'ouvrent dans votre application
Swift 3 Exemple pour détecter les actions sur les taps 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 wise, vous pouvez ajouter n'importe quelle action que vous voulez avec la méthode shouldInteractWith URL
UITextFieldDelegate.
santé!!
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
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 ça:
NSString *fullString = @"A man who bought the Google.com domain name for 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];
il suffit de trouver une solution sans code pour UITextView:
Activer la Détection->les Liens les options, l'URL et aussi e-mail sera détecté et cliquable!!!
mise à jour:
il y avait 2 parties clés à ma question:
- Comment faire un lien où le texte affiché pour le lien cliquable est différent du lien réel qui est appelé:
- comment configurer les liens sans avoir à utiliser le code personnalisé pour définir les attributs sur le texte.
il s'avère que iOS 7 a ajouté la capacité de charger le texte attribué de NSData
.
j'ai créé une sous-classe personnalisée de UITextView
qui tire parti de l'attribut @IBInspectable
et vous permet de charger le contenu d'un fichier RTF directement dans IB. Vous tapez simplement le nom du fichier dans IB et la classe custom fait le reste.
Voici les détails:
Dans iOS 7, NSAttributedString
acquis de 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, utilisez ensuite initWithData:options:documentAttributes:error:
pour charger cette NSData dans votre vue texte. (Notez qu'il existe aussi 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épréciée dans iOS 9. Il est plus sûr d'utiliser la méthode initWithData:options:documentAttributes:error:
, qui n'a pas été dépréciée.
je voulais une méthode qui me permettait d'installer des liens cliquables dans mes vues texte sans avoir à créer de code spécifique aux liens que j'utilisais.
le la solution que j'ai trouvée était de créer une sous-classe personnalisée D'UITextView que j'appelle RTF_UITextView
et lui donner une propriété @IBInspectable
appelé RTF_Filename
. L'ajout de l'attribut @IBInspectable
à une propriété amène Interface Builder à exposer cette propriété dans L'attribut" Inspector."Vous pouvez ensuite définir cette valeur à partir du code personnalisé IB wihtout.
j'ai aussi ajouté un attribut @IBDesignable
à ma classe personnalisée. L'attribut @IBDesignable
indique à Xcode qu'il doit installer un lancer la copie de votre classe de vue personnalisée dans le constructeur D'Interface pour que vous puissiez la voir dans l'affichage graphique de votre hiérarchie de vue. () Malheureusement, pour cette classe, la propriété @IBDesignable
semble être feuilletée. Cela a fonctionné quand je l'ai ajouté pour la première fois, mais ensuite j'ai supprimé le contenu de texte simple de ma vue de texte et les liens cliquables dans ma vue ont disparu et je n'ai pas été en mesure de les récupérer.)
le code de mon RTF_UITextView
est très simple. En plus de l'ajout de l' @IBDesignable
et une propriété RTF_Filename
avec l'attribut @IBInspectable
, j'ai ajouté une méthode didSet()
à la propriété RTF_Filename
. La méthode didSet()
est appelée chaque fois que la valeur de la propriété RTF_Filename
change. Le code de la méthode didSet()
est très 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 va pas vous permettre de manière fiable de prévisualiser votre texte stylé dans Interface builder alors il pourrait être préférable pour configurer le code ci-dessus comme une extension D'UITextView plutôt que comme une sous-classe personnalisée. De cette façon, vous pouvez l'utiliser dans n'importe quel texte vue sans avoir à modifier l'affichage de texte de la classe personnalisée.
Voir mon autre réponse si vous avez besoin de prendre en charge les versions iOS avant iOS 7.
vous pouvez télécharger un exemple de projet qui inclut cette nouvelle classe de gitHub:
DatesInSwift projet de démonstration sur Github
Version Swift:
// 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
j'avais besoin de continuer à utiliser un UILabel pur, ainsi appelé cela de mon tap recognizer (ceci est basé sur la réponse de malex ici: index de 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];
}
}];
un ajout rapide à la description originale de Duncan c vis-à-vis du comportement IB vie. Il écrit: "c'est trivial de rendre les hyperliens cliquables dans un UITextView. Vous venez de placer la case à cocher" detect links " sur la vue en 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ébloquer le comportement" éditable " pour que les urls soient détectées et cliquables.
si vous voulez utiliser le nom NSLinkAttributeName dans UITextView, alors vous pouvez envisager D'utiliser la bibliothèque AttributedTextView. C'est une sous-classe UITextView qui le rend très facile à manipuler. Pour plus d'informations voir: https://github.com/evermeer/AttributedTextView
vous pouvez faire interagir n'importe quelle partie du texte 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 la manipulation des hashtags et des mentions vous pouvez utiliser le code suivant:
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 })
}
l'excellente bibliothèque de @AliSoftware OHAttributedStringAdditions
permet d'ajouter facilement des liens dans UILabel
voici la documentation: https://github.com/AliSoftware/OHAttributedStringAdditions/wiki/link-in-UILabel
si vous voulez un substrat actif dans votre UITextView, vous pouvez utiliser mon TextView étendu... sa courte et simple. Vous pouvez le modifier comme vous le souhaitez.
CodeNSMutableAttributedString *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;
KEY POINTS:
- 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.
utilisez UITextView et définissez des types datadetectort pour le lien.
comme ceci:
testTextView.editable = false
testTextView.dataDetectorTypes = .link
si vous souhaitez détecter lien, numéro de téléphone,adresse, etc..puis
testTextView.dataDetectorTypes = .all