Как я могу сделать ссылку на клики в NSAttributedString?

Тривиально сделать гиперссылки интерактивными в UITextView . Вы просто установите флажок «обнаружить ссылки» в представлении в IB, и он обнаруживает ссылки HTTP и превращает их в гиперссылки.

Однако это все равно означает, что то, что видит пользователь, является «сырой» ссылкой. RTF-файлы и HTML-файлы позволяют вам настраивать читаемую пользователем строку со ссылкой «позади».

Легко установить атрибутный текст в текстовое представление (или UILabel или UITextField , если на то пошло.) Однако, когда этот атрибутный текст включает ссылку, он не UITextField для UITextField .

Есть ли способ сделать читаемый пользователем текст интерактивным в UITextView , UILabel или UITextField ?

Разметка отличается от SO, но вот общая идея. Я хочу, чтобы текст был следующим:

Этот морфинг был создан с помощью Face Dancer , нажмите для просмотра в магазине приложений.

Единственное, что я могу получить, это:

Этот морфинг был создан с помощью Face Dancer, нажмите на http://example.com/facedancer для просмотра в магазине приложений.

    Используйте NSMutableAttributedString .

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

    Изменить :

    Это не касается непосредственно вопроса, а просто для уточнения, UITextField и UILabel не поддерживают открывающие URL-адреса. Если вы хотите использовать UILabel со ссылками, вы можете проверить TTTAttributedLabel .

    Также вы должны установить значение dataDetectorTypes вашего UITextView в UIDataDetectorTypeLink или UIDataDetectorTypeAll чтобы открыть URL-адреса при нажатии. Или вы можете использовать метод делегата, как это предлагается в комментариях.

    Я нашел это очень полезным, но мне нужно было сделать это в нескольких местах, поэтому я завернул свой подход в простом расширении до 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 } } 

    Пример использования:

     let attributedString = NSMutableAttributedString(string:"I love stackoverflow!") let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com") if linkWasSet { // adjust more attributedString properties } 

    Objective-C

    Я просто применил требование сделать то же самое в чистом проекте Objective-C, так что вот категория 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 

    Пример использования:

     NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"]; BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"]; if (linkWasSet) { // adjust more attributedString properties } 

    Незначительное улучшение решения ujell: если вы используете NSURL вместо NSString, вы можете использовать любой URL (например, пользовательские URL-адреса)

     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; 

    Повеселись!

    Я просто создал подclass UILabel, чтобы специально рассмотреть такие варианты использования. Вы можете легко добавить несколько ссылок и определить для них разные обработчики. Он также поддерживает выделение нажатой ссылки, когда вы касаетесь сенсорной обратной связи. См. https://github.com/null09264/FRHyperLabel .

    В вашем случае код может выглядеть так:

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

    Пример скриншота (обработчик настроен на появление предупреждения вместо того, чтобы открыть URL-адрес в этом случае)

    facedancer

    У меня тоже было подобное требование, изначально я использовал UILabel, а затем понял, что UITextView лучше. Я заставил UITextView вести себя как UILabel, отключив взаимодействие и прокрутку и сделав метод категории для NSMutableAttributedString чтобы установить ссылку на текст так же, как и то, что сделал Карл (+1 для этого), это моя версия 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]; } } 

    вы можете использовать нижеприведенный делегат, чтобы обработать действие

     - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange { // do the task return YES; } 

    Используйте UITextView, он поддерживает интерактивные ссылки. Создайте присвоенную строку, используя следующий код

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

    Затем установите текст UITextView следующим образом

     NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor], NSUnderlineColorAttributeName: [UIColor blueColor], NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)}; customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links textView.attributedText = attributedString; 

    Убедитесь, что вы активируете поведение «Выбор» UITextView в XIB.

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

    Наконец я понял, как это сделать. Проблема в том, что IB не соблюдает встроенные ссылки.

    Кроме того, версия iOS версии NSAttributedString не позволяет инициализировать строку с атрибутом RTF-файла. Версия OS X версии NSAttributedString имеет инициализатор, который принимает RTF-файл в качестве входных данных.

    NSAttributedString соответствует протоколу NSCoding, поэтому вы можете преобразовать его в / из NSData

    Я создал инструмент командной строки OS X, который принимает RTF-файл в качестве входных данных и выводит файл с расширением .data, который содержит NSData из NSCoding. Затем я помещаю файл .data в свой проект и добавляю пару строк кода, которые загружают текст в представление. Код выглядит так (этот проект был в 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 } 

    Для приложений, использующих много форматированного текста, я создаю правило сборки, которое сообщает Xcode, что все файлы .rtf в данной папке являются исходными, а файлы .data являются результатом. Как только я это сделаю, я просто добавляю файлы .rtf в указанный каталог (или редактирую существующие файлы), и процесс сборки показывает, что они являются новыми / обновленными, запускает инструмент командной строки и копирует файлы в пакет приложений. Он работает красиво.

    Я написал сообщение в блоге, которое ссылается на образец (Swift), демонстрирующий технику. Вы можете видеть это здесь:

    Создание интерактивных URL-адресов в UITextField, которые открываются в вашем приложении

    Пример Swift 3 для обнаружения действий по атрибутным текстовым отводам

    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 } 

    Как мудрый вы можете добавить любое действие, которое вы хотите, с shouldInteractWith URL UITextFieldDelegate метод.

    Ура !!

    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 

    Я написал метод, который добавляет ссылку (linkString) в строку (fullString) с определенным 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; } 

    Вы должны называть это следующим образом:

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

    Просто найдите бесплатное решение для UITextView: введите описание изображения здесь

    Включить опцию «Обнаружение->», URL-адрес и адрес электронной почты будут обнаружены и доступны для просмотра!

    Обновить:

    На мой вопрос было две ключевые части:

    1. Как сделать ссылку, где текст, показанный для ссылки с возможностью клика, отличается от фактической ссылки, которая вызывается:
    2. Как настроить ссылки, не используя специальный код для установки атрибутов в тексте.

    Оказывается, что iOS 7 добавила возможность загружать атрибутный текст из NSData .

    Я создал собственный подclass UITextView который использует атрибут @IBInspectable и позволяет загружать содержимое из RTF-файла непосредственно в IB. Вы просто вводите имя файла в IB, а пользовательский class делает все остальное.

    Вот подробности:

    В iOS 7 NSAttributedString получил метод initWithData:options:documentAttributes:error: Этот метод позволяет загружать NSAttributedString из объекта NSData. Вы можете сначала загрузить RTF-файл в NSData, а затем использовать initWithData:options:documentAttributes:error: загрузить NSData в текстовое представление. (Обратите внимание, что существует также метод initWithFileURL:options:documentAttributes:error: будет загружать атрибутную строку непосредственно из файла, но этот метод устарел в iOS 9. Безопаснее использовать метод initWithData:options:documentAttributes:error: , который не был устаревшим.

    Мне нужен метод, который позволяет мне устанавливать интерактивные ссылки в мои текстовые представления без необходимости создавать какой-либо код для ссылок, которые я использовал.

    Решение, с которым я столкнулся, заключалось в создании пользовательского подclassа UITextView, который я вызываю RTF_UITextView и предоставляю ему свойство RTF_Filename называемое RTF_Filename . Добавление атрибута @IBInspectable к свойству заставляет Interface Builder выставлять это свойство в «Attributes Inspector». Затем вы можете установить это значение из IB без специального кода.

    Я также добавил атрибут @IBDesignable в свой собственный class. @IBDesignable сообщает Xcode, что он должен установить @IBDesignable копию вашего пользовательского classа вида в конструктор интерфейса, чтобы вы могли видеть его на графическом дисплее иерархии представлений. () К сожалению, для этого classа свойство @IBDesignable кажется flaky. Это сработало, когда я впервые добавил его, но затем я удалил текстовое содержимое своего текстового представления, а ссылки, доступные по ссылке, на моем пути исчезли, и я не смог их вернуть.)

    Код для моего RTF_UITextView очень прост. В дополнение к добавлению атрибута RTF_Filename свойства @IBInspectable атрибутом @IBInspectable , я добавил метод didSet() в свойство RTF_Filename . Метод didSet() любое время, когда изменяется RTF_Filename свойства RTF_Filename . Код метода didSet() довольно прост:

     @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."); } } } } } 

    Обратите внимание, что если свойство @IBDesignable не позволяет надежно разрешить вам просмотр вашего стилизованного текста в построителе интерфейса, возможно, было бы лучше установить вышеуказанный код как расширение UITextView, а не пользовательский подclass. Таким образом, вы можете использовать его в любом текстовом представлении без необходимости изменять текстовое представление в пользовательский class.

    См. Мой другой ответ, если вам нужно поддерживать версии iOS до iOS 7.

    Вы можете загрузить образец проекта, который включает этот новый class из gitHub:

    Демо-проект DatesInSwift на Github

    Быстрая версия:

      // 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 

    Мне нужно было использовать чистую UILabel, так называемую это из моего распознавателя отводов (это основано на ответе malex здесь: Индекс символов в точке касания для 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 * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) { if (attrs[NSLinkAttributeName]) { NSString* urlString = attrs[NSLinkAttributeName]; NSURL* url = [NSURL URLWithString:urlString]; [[UIApplication sharedApplication] openURL:url]; } }]; 

    Быстрое добавление к оригинальному описанию Duncan C vis-á-vie IB. Он пишет: «Тривиально сделать гиперссылки интерактивными в UITextView. Вы только установили флажок« обнаружить ссылки »на представлении в IB, и он обнаруживает http-ссылки и превращает их в гиперссылки».

    Мой опыт (по крайней мере, в xcode 7) заключается в том, что вам также нужно отключить поведение «Редактируемый» для выявления и использования URL-адресов.

    Если вы хотите использовать NSLinkAttributeName в UITextView, вы можете использовать библиотеку AttributedTextView. Это подclass UITextView, который упрощает их обработку. Для получения дополнительной информации см. https://github.com/evermeer/AttributedTextView

    Вы можете сделать любую часть текста так, как это (где textView1 является 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) 

    А для обработки hashтегов и упоминаний вы можете использовать такой код:

     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 }) } 

    Отличная библиотека от @AliSoftware OHAttributedStringAdditions упрощает добавление ссылок в UILabel вот документация: https://github.com/AliSoftware/OHAttributedStringAdditions/wiki/link-in-UILabel

    если вы хотите использовать активную подстроку в UITextView, вы можете использовать мой расширенный TextView … его короткий и простой. Вы можете отредактировать его, как хотите.

    результат: введите описание изображения здесь

    код: https://github.com/marekmand/ActiveSubstringTextView

     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; 

    ОСНОВНЫЕ ПУНКТЫ:

    • Убедитесь, что вы активируете поведение «Выбор» UITextView в XIB.
    • Убедитесь, что вы отключили поведение «Редактируемое» UITextView в XIB.

    Используйте UITextView и установите dataDetectorTypes для Link.

    как это:

     testTextView.editable = false testTextView.dataDetectorTypes = .link 

    Если вы хотите обнаружить ссылку, номер телефона, адрес и т. Д., Тогда

     testTextView.dataDetectorTypes = .all 
    Давайте будем гением компьютера.