Как я могу сделать кликабельную ссылку в NSAttributedString?
это тривиально, чтобы сделать ссылки кликабельными в UITextView
. Вы просто устанавливаете флажок "обнаруживать ссылки" на представлении В IB, и он обнаруживает HTTP-ссылки и превращает их в гиперссылки.
однако это все равно означает, что пользователь видит "сырую" ссылку. Файлы RTF и HTML позволяют вам настроить читаемую пользователем строку со ссылкой " за " ней.
легко установить приписанный текст в текстовое представление (или UILabel
или UITextField
, если уж на то пошло.) Однако, когда этот приписанный текст включает ссылку, она не является кликабельной.
есть ли способ сделать читаемый пользователем текст кликабельным в UITextView
,UILabel
или UITextField
?
разметка отличается на SO, но вот общая идея. То, что я хочу, это текст, как это:
этот морф был создан с Многоликим Нажмите для просмотра в магазине приложений.
единственное, что я могу сделать это:
этот морф был создан с Face Dancer, нажмите наhttp://example.com/facedancer для просмотра в магазине приложений.
20 ответов:
использовать NSMutableAttributedString.
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"]; [str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)]; yourTextView.attributedText = str;
Edit:
речь идет не непосредственно о вопросе, а просто для уточнения,
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 категория.
@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;
удачи!
Я только что создал подкласс 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-адреса в этот случай)
у меня тоже было подобное требование, изначально я использовал 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; }
Use 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 XNSAttributedString
тут есть инициализатор, который принимает RTF-файл в качестве входных данных.
NSAttributedString
соответствует протоколу NSCoding, поэтому вы можете конвертировать его в / из NSDataЯ создал инструмент командной строки OS X, который принимает RTF-файл в качестве входного и выводит файл с расширением .данные, которые содержат NSData из NSCoding. Я тогда положил .файл данных в мой проект и добавить пару строк кода, который загружает текст в поле зрения. Код выглядит так (этот проект был в Свифт):
/* 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 файлы в данной папке и источник .файлы данных являются выходными данными. Как только я это сделаю, я просто добавлю .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 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];
обновление:
было 2 ключевых части моего вопроса:
- как сделать ссылку, где текст, показанный для кликабельной ссылки, отличается от фактической ссылки, которая вызывается:
- как настроить ссылки без использования пользовательского кода для установки атрибутов в тексте.
оказывается, в iOS 7 добавлена возможность загружать приписанный текст из
NSData
.я создал пользовательский подкласс
UITextView
это использует в своих интересах@IBInspectable
атрибут и позволяет загружать содержимое из файла RTF непосредственно в IB. Вы просто вводите имя файла в IB, а пользовательский класс делает все остальное.вот подробности:
в 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:
, который не был устаревшим.мне нужен метод, который позволит мне устанавливать кликабельные ссылки в мои текстовые представления без необходимости создавать какой-либо код, специфичный для ссылок, которые я использовал.
решение, которое я придумал, состояло в том, чтобы создать пользовательский подкласс UITextView, который я называю
RTF_UITextView
С@IBInspectable
свойствоRTF_Filename
. Добавление@IBInspectable
атрибут к свойству заставляет Interface Builder предоставлять это свойство в " Инспекторе атрибутов."Затем вы можете установить это значение из IB без пользовательского кода.я также добавил
@IBDesignable
атрибут для моего пользовательского класса. Элемент@IBDesignable
атрибут сообщает Xcode, что он должен установить запущенную копию вашего пользовательского класса представления в Interface builder, чтобы вы могли видеть ее в графическом отображении иерархии представлений. ()К сожалению, для этого класса,@IBDesignable
собственность кажется шелушащейся. Он работал, когда я впервые добавил его, но затем я удалил текстовое содержимое моего текстового представления, и кликабельные ссылки в моем представлении исчезли, и я не смог их вернуть.)код для моего
RTF_UITextView
очень удобно. В дополнение к добавлению иRTF_Filename
собственность с@IBInspectable
атрибут, я добавилdidSet()
методRTF_Filename
собственность. ЭлементdidSet()
метод вызывается в любое время значение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 не позволяет надежно просматривать стилизованный текст в Interface builder, то может быть лучше установить приведенный выше код как расширение UITextView, а не пользовательский подкласс. Таким образом, вы можете использовать его в любом текстовом представлении, не меняя текстовое представление на пользовательский класс.
смотрите мой другой ответ, Если вам нужно поддержать версии iOS до iOS 7.
вы можете скачать пример проекта, который включает в себя этот новый класс из gitHub:
демо-проект DatesInSwift на Github
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
Мне нужно было продолжать использовать чистый 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<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]; } }];
быстрое дополнение к оригинальному описанию Дункана C в отношении поведения IB. Он пишет: "тривиально сделать гиперссылки кликабельными в UITextView. Вы просто устанавливаете флажок "обнаруживать ссылки" на представлении В IB, и он обнаруживает http-ссылки и превращает их в гиперссылки."
мой опыт (по крайней мере, в xcode 7) заключается в том, что вам также нужно отключить "редактируемое" поведение для URL-адресов, которые будут обнаружены и доступны для клика.
Если вы хотите использовать NSLinkAttributeName в UITextView, то вы можете рассмотреть возможность использования библиотеки AttributedTextView. Это подкласс 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)
и для обработки хэштегов и упоминаний вы можете использовать такой код, как это:
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... его короткий и простой. Вы можете редактировать его, как вы хотите.
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.