Jak mogę utworzyć klikalny link w NSAttributString?

200

Łatwo jest klikać hiperłącza w pliku UITextView. Po prostu ustawiłeś pole wyboru „wykrywaj łącza” w widoku w IB, który wykrywa łącza HTTP i zamienia je w hiperłącza.

Jednak nadal oznacza to, że użytkownik widzi link „raw”. Zarówno pliki RTF, jak i HTML umożliwiają ustawienie czytelnego dla użytkownika ciągu z linkiem „za” nim.

Łatwo jest zainstalować przypisany tekst w widoku tekstu (lub a UILabellubUITextField , jeśli o to chodzi). Jednak gdy ten przypisany tekst zawiera link, nie można go kliknąć.

Czy istnieje sposób, aby tekst czytelny dla użytkownika był klikalny w UITextView, UILabellubUITextField ?

Znaczniki różnią się w przypadku SO, ale oto ogólny pomysł. Chcę tylko takiego tekstu:

Ta przemiana została wygenerowana za pomocą Face Dancer , kliknij, aby wyświetlić w App Store.

Jedyne, co mogę uzyskać, to:

Ta przemiana została wygenerowana za pomocą Face Dancer. Kliknij http://example.com/facedancer, aby wyświetlić w sklepie z aplikacjami.

Duncan C.
źródło
Wypróbuj tę próbkę .. IFTweetLabel Mam nadzieję, że to pomoże ..
Vidhyanand
Dobra robota w mgnieniu oka powyżej 100K. Witamy w klubie 100K. Zasłużone!
vacawama
@vacawama, czekaj, kiedy to się stało? Ostatni raz patrzyłem na ≈98k! (Słyszę pogłoski, że dostałeś trochę łupu jako członek klubu 100k?)
Duncan C
Zmienili głosowanie na pytania z +5 na +10, więc gdybyś miał 800 głosów pozytywnych, natychmiast zarobiłbyś +4000. Nadal czekam na 100 tys. Łupów (skrzyżowane w kwietniu). Coś o zmianie dostawców swag ...
swagów

Odpowiedzi:

156

Użyj NSMutableAttributString .

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

Edytuj :

Nie chodzi tu bezpośrednio o pytanie, ale jedynie o wyjaśnienie UITextFieldi UILabelnie obsługuje otwierania adresów URL. Jeśli chcesz używać UILabelz linkami, możesz sprawdzić TTTAttributLabel .

Powinieneś także ustawić dataDetectorTypesswoją wartość UITextViewnaUIDataDetectorTypeLink lub UIDataDetectorTypeAlldo otwartych URL po kliknięciu. Lub możesz użyć metody delegowania zgodnie z sugestiami w komentarzach.

ujell
źródło
7
Tak, działa, po prostu umieść go w UITextView i zastąp metodę delegowania: - (BOOL) textView: (UITextView *) textView shouldInteractWithURL: (NSURL *) url inRange: (NSRange) characterRange
Yunus Nedim Mehel
To nie działa w UILabel - nic nie dzieje się po dotknięciu pola.
Jack BeNimble
7
@saboehnke masz na myśli wywołanie metody po kliknięciu linku? jeśli tak, zastosuj metodę delegowania, podaj - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
atrapowy
2
Nie wiem jak to działa. Wartość atrybutu powinna być typu NSURL. ----[str addAttribute: NSLinkAttributeName value: [NSURL URLWithString:@"http://www.google.com"] range: NSMakeRange(0, str.length)];
Nirav Dangi
1
@NiravDangi zNSAttributedString.h UIKIT_EXTERN NSString * const NSLinkAttributeName NS_AVAILABLE(10_0, 7_0); // NSURL (preferred) or NSString
Ahmed Nawar
142

Uznałem to za bardzo przydatne, ale musiałem to zrobić w kilku miejscach, więc swoje podejście zawarłem w prostym rozszerzeniu do NSMutableAttributedString:

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

Przykładowe użycie:

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

if linkWasSet {
    // adjust more attributedString properties
}

Cel C

Właśnie spełniłem wymóg, aby zrobić to samo w czystym projekcie Objective-C, więc oto kategoria 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

Przykładowe użycie:

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

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

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

Upewnij się, że atrybut Zachowania NSTextField jest ustawiony jako Dostępny. Atrybut zachowania Xcode NSTextField

Karl Nosworthy
źródło
Byłby bardzo wdzięczny przykład szybkiego użycia / implementacji tego.
ioopl
3
@ioop. Dodałem bardzo mały przykład do oryginalnego postu powyżej, mam nadzieję, że to pomaga.
Karl Nosworthy
7
To działało poprawnie. Chcę tylko powiedzieć, że musisz ustawić opcję UITextView, aby umożliwić kliknięcie linku
lujop
1
@felecia gene, zarówno w implementacji Celu C, jak i Szybkiej metoda zwraca wynik boolowski wskazujący, czy miało miejsce dopasowanie i wynikowy zestaw. Występujący błąd polega na tym, że nie rejestrujesz tego wyniku - co jest w porządku. Możesz uchwycić ten wynik, przypisując go do zmiennej lokalnej lub dostosować metodę, aby zatrzymać zwracanie wartości boolowskiej, jeśli lepiej odpowiada twoim potrzebom. Mam nadzieję że to pomogło?
Karl Nosworthy,
1
Nie ma problemu @ feleciagenet, dodałem przechowywanie i sprawdzanie wyniku metody do przykładów Swift i ObjectiveC.
Karl Nosworthy,
34

Właśnie utworzyłem podklasę UILabel, aby specjalnie adresować takie przypadki użycia. Możesz łatwo dodawać wiele linków i definiować dla nich różne programy obsługi. Obsługuje także podświetlanie naciśniętego łącza po dotknięciu w celu uzyskania informacji zwrotnej. Proszę odnieść się do https://github.com/null09264/FRHyperLabel .

W twoim przypadku kod może się podobać:

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

Przykładowy zrzut ekranu (w tym przypadku moduł obsługi ustawia ostrzeżenie zamiast otwierać adres URL)

tancerz

Jinghan Wang
źródło
jeśli przypuśćmy, że mój tekst jest taki: Ta przemiana została wygenerowana za pomocą Face Dancer, kliknij widok twarzy w tancerzu w App Store Face Dancer. tutaj mam 3 Face Dancer, to nie działało
MANCHIKANTI KRISHNAKISHORE,
1
W takim przypadku użyj - (void)setLinkForRange:(NSRange)range withLinkHandler:(void(^)(FRHyperLabel *label, NSRange selectedRange))handler; zamiast tego interfejsu API . Proszę odnieść się do pliku Readme na stronie github.
Jinghan Wang
1
Wydaje się, że FRHyperLabel już nie działa. Wewnątrz „characterIndexForPoint:” zawsze zwraca -1 (nie znaleziono).
John Pang,
Nie działa dla mnie w przypadku multiline label. Wykrywanie znaków jest nieprawidłowe. 15-znakowy ciąg linku jest klikalny tylko dla niektórych pierwszych znaków, inne postacie nic nie robią
Accid Bright
27

Niewielkie udoskonalenie rozwiązania ujell: jeśli używasz NSURL zamiast NSString, możesz użyć dowolnego adresu URL (np. Niestandardowych adresów 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;

Baw się dobrze!

Hans One
źródło
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
źródło
Ta odpowiedź działa idealnie taka, jaka jest. Wygląda na to, że nie potrzebujesz żadnej z podklas kolorystycznych ani niestandardowych używanych przez inne odpowiedzi.
zeroimpl
19

Ja również miałem podobne wymagania, początkowo korzystałem z UILabel, a potem zdałem sobie sprawę, że UITextView jest lepszy. Sprawiłem, że UITextView zachowuje się jak UILabel, wyłączając interakcję i przewijanie, i stworzyłem metodę kategorii, NSMutableAttributedStringaby ustawić link do tekstu tak samo jak to, co zrobił Karl (+1 za to), to jest moja wersja objc

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

możesz użyć poniższego delegata, aby obsłużyć akcję

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
    // do the task
    return YES;
}
anoop4real
źródło
1
O ile mogę stwierdzić, ustawienie NSForegroundColorAttributeNamew zakresie, w którym NSLinkAttributeNamezastosowano, nie działa. Nie ma znaczenia co do zawartości, linkTextAttributesz UITextViewsą stosowane zamiennie. Czy NSForegroundColorAttributeNamedziała dla ciebie?
Dima,
Czy na pewno nie nastawiasz się linkTextAttributesna to samo? a może tintColor? Czy jesteś w stanie sprawić, aby 2 linki były wyświetlane w różnych kolorach w tym samym widoku tekstu?
Dima,
1
Oto działający kod NSRange range = [self.text rangeOfString: textToFind options: NSCaseInsensitiveSearch]; if (range.location! = NSNotFound) {NSMutableAttributString * string = [[NSMutableAttributString przydział] initWithString: self.text]; [ciąg addAttribute: NSLinkAttributeName wartość: zakres adresów URL: zakres]; [ciąg addAttribute: NSForegroundColorAttributeName wartość: [UIColor blueColor] zakres: zakres]; self.text = @ ""; self.attributeText = ciąg; }
Nosov Pavel,
16

Użyj UITextView obsługuje linki klikalne. Utwórz przypisany ciąg przy użyciu następującego kodu

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

Następnie ustaw tekst UITextView w następujący sposób

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

                                 NSUnderlineColorAttributeName: [UIColor blueColor],

                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

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

Upewnij się, że w XIB włączono „wybieralne” zachowanie UITextView.

Nitheesh George
źródło
15
Myślę, że to najlepsze rozwiązanie! Uwaga na temat włączania Selectablejest ważna!
LunaCodeGirl
To nie podkreśliło linku dla mnie (iOS 7, 8). Musiałem użyć NSUnderlineStyleAttributeName: [NSNumber numberWithInt: NSUnderlineStyleSingle]
prewett
1
umożliwienie wyboru jest najważniejszą i nieintuicyjną informacją!
Nicolas Massart,
13

Sercem mojego pytania było to, że chciałem móc tworzyć klikalne łącza w widokach / polach / etykietach tekstu bez konieczności pisania niestandardowego kodu do manipulowania tekstem i dodawania łączy. Chciałem, aby był oparty na danych.

W końcu wymyśliłem, jak to zrobić. Problem polega na tym, że IB nie honoruje osadzonych linków.

Co więcej, wersja iOS NSAttributedStringnie pozwala na inicjalizację przypisanego ciągu z pliku RTF. OS X wersja NSAttributedString ma mieć inicjator która pobiera plik RTF jako wejście.

NSAttributedString jest zgodny z protokołem NSCoding, dzięki czemu można przekonwertować go na / z NSData

Utworzyłem narzędzie wiersza polecenia OS X, które pobiera plik RTF jako dane wejściowe i wysyła plik z rozszerzeniem .data, który zawiera NSData z NSCoding. Następnie wstawiam plik .data do mojego projektu i dodaję kilka wierszy kodu, które ładują tekst do widoku. Kod wygląda następująco (ten projekt był w 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
}

W przypadku aplikacji, które używają dużo sformatowanego tekstu, tworzę regułę kompilacji, która mówi Xcode, że wszystkie pliki .rtf w danym folderze są źródłowe, a pliki .data są danymi wyjściowymi. Kiedy to zrobię, po prostu dodaję pliki .rtf do wyznaczonego katalogu (lub edytuję istniejące pliki), a proces kompilacji dowiaduje się, że są one nowe / aktualizowane, uruchamia narzędzie wiersza poleceń i kopiuje pliki do pakietu aplikacji. Działa pięknie.

Napisałem post na blogu, który prowadzi do przykładowego (Swift) projektu demonstrującego tę technikę. Możesz to zobaczyć tutaj:

Tworzenie klikalnych adresów URL w polu UITextField, które otwierają się w Twojej aplikacji

Duncan C.
źródło
11

Przykład szybkiego 3, aby wykryć działania na przypisanych dotknięciach tekstu

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
}

Podobnie, możesz dodać dowolną akcję za shouldInteractWith URLpomocą metody UITextFieldDelegate.

Twoje zdrowie!!

Akila Wasala
źródło
7

Szybką odpowiedzią jest użycie UITextView zamiast UILabel. Musisz włączyć Selectablei wyłączyć Editable.

Następnie wyłącz wskaźniki przewijania i odbicia.

Zrzut ekranu

Zrzut ekranu

Moje rozwiązanie za pomocą NSMutableAttributedStringciągu 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
źródło
To. Byłem w stanie odczytać plik RTF z mojego pakietu zasobów, przekonwertować go NSAttributedString, ustawić jako attributedTextmój UITextViewi hiperłącza po prostu działają! Znalezienie zakresu każdego hiperłącza i skonfigurowanie go za pomocą atrybutów byłoby dużo pracy.
Nicolas Miari
6

Napisałem metodę, która dodaje link (linkString) do ciągu (fullString) z pewnym adresem 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;
}

Powinieneś to tak nazwać:

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
źródło
Można kliknąć, ale nie otwiera linku ani niczego. po prostu klika jak przycisk, który nic nie robi.
Reza.Ab
5

Musiałem nadal używać czystego UILabel, tak zwanego tego z mojego rozpoznawania zaczepów (jest to oparte na odpowiedzi malex tutaj: Indeks znaków w punkcie dotykowym dla 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
źródło
Było to bardzo pomocne, nie mogłem uzyskać indeksów od znaków w ostatnim wierszu. Twój kod ma +100 na textContainer podczas inicjowania CGSize, co nie ma dla mnie większego sensu, ale udało się.
blueether
4

Aktualizacja:

Moje pytanie zawierało 2 kluczowe części:

  1. Jak utworzyć łącze, w którym tekst wyświetlany dla łącza, który można kliknąć, różni się od rzeczywistego wywołanego łącza:
  2. Jak skonfigurować łącza bez konieczności używania niestandardowego kodu w celu ustawienia atrybutów w tekście.

Okazuje się, że iOS 7 dodał możliwość ładowania przypisanego tekstu NSData.

Stworzyłem niestandardową podklasę, UITextViewktóra korzysta z@IBInspectable atrybutu i pozwala ładować zawartość z pliku RTF bezpośrednio w IB. Po prostu wpisz nazwę pliku w IB, a klasa niestandardowa zajmie się resztą.

Oto szczegóły:

W iOS 7 NSAttributedStringuzyskał tę metodę initWithData:options:documentAttributes:error:. Ta metoda pozwala załadować NSAttributString z obiektu NSData. Możesz najpierw załadować plik RTF do NSData, a następnie użyć initWithData:options:documentAttributes:error:do załadowania tego NSData do widoku tekstowego. (Należy pamiętać, że istnieje również metoda initWithFileURL:options:documentAttributes:error:, która załaduje przypisany ciąg bezpośrednio z pliku, ale ta metoda została wycofana w iOS 9. Bezpieczniej jest użyć tej metody initWithData:options:documentAttributes:error:, która nie była przestarzała.

Chciałem metody, która pozwoli mi zainstalować klikalne linki w moich widokach tekstowych bez konieczności tworzenia kodu specyficznego dla linków, których używałem.

Rozwiązaniem, które wymyśliłem, było utworzenie niestandardowej podklasy wywoływanej przez UITextView RTF_UITextViewi nadanie jej @IBInspectablewłaściwości o nazwie RTF_Filename. Dodawanie@IBInspectable atrybutu do właściwości powoduje, że Konstruktor interfejsu ujawnia tę właściwość w „Inspektorze atrybutów”. Następnie możesz ustawić tę wartość z IB bez kodu niestandardowego.

Dodałem również @IBDesignableatrybut do mojej klasy niestandardowej. @IBDesignableAtrybut Xcode mówi, że powinien on zainstalować uruchomioną kopię widoku niestandardowego interfejsu klasy do konstruktora, więc można zobaczyć go w graficznym wyświetlaczu widzenia hierarchii. () Niestety, dla tej klasy @IBDesignablewłaściwość wydaje się niestabilna. Działało, kiedy go po raz pierwszy dodałem, ale potem usunąłem zwykły tekst z mojego widoku tekstowego, a klikalne łącza w moim widoku zniknęły i nie byłem w stanie ich odzyskać).

Mój kod RTF_UITextViewjest bardzo prosty. Oprócz dodania @IBDesignableatrybutu i RTF_Filenamewłaściwości do @IBInspectableatrybutu dodałem didSet()metodę do RTF_Filenamewłaściwości. didSet()Metoda jest wywoływana w dowolnym momencie wartość RTF_Filenamezmian własności. Kod didSet()metody jest dość prosty:

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

Zauważ, że jeśli właściwość @IBDesignable nie pozwoli w niezawodny sposób wyświetlić podglądu stylizowanego tekstu w Konstruktorze interfejsów, może być lepiej ustawić powyższy kod jako rozszerzenie UITextView zamiast niestandardowej podklasy. W ten sposób można go używać w dowolnym widoku tekstu bez konieczności zmiany widoku tekstu na klasę niestandardową.

Zobacz moją inną odpowiedź, jeśli potrzebujesz obsługiwać wersje iOS wcześniejsze niż iOS 7.

Możesz pobrać przykładowy projekt zawierający tę nową klasę z gitHub:

Projekt demonstracyjny DatesInSwift na Github

Duncan C.
źródło
3

Po prostu znajdź rozwiązanie bez kodu dla UITextView: wprowadź opis zdjęcia tutaj

Włącz wykrywanie-> Opcje linków, adres URL oraz adres e-mail zostaną wykryte i klikalne!

Bill Chan
źródło
3
Dzięki temu linki można klikać. Chcę mieć czytelny dla użytkownika tekst z linkiem. Zobacz przykład w moim pierwotnym pytaniu.
Duncan C
Tak, moja odpowiedź dotyczy tylko przypadku, gdy link jest taki sam jak tekst. Jeśli link jest inną rzeczą, postąpiłbym zgodnie z odpowiedzią @ ujell.
Bill Chan,
3
Moje pytanie dotyczyło konkretnie klikalnego tekstu, który wyświetla coś innego niż adres URL. Nie zrobiłeś nic poza spojrzeniem na pytanie, prawda?
Duncan C
1
nie służyłem innym celom, ale z pewnością to właśnie przyszedłem do stosu szukając ... sposobu, aby linki w mojej aplikacji czatu były klikalne. Bingo Znalazłem ten artykuł ... dzięki! Wish xcode pozwoliłby na włączenie twittera i hash tagu
MizAkita,
Działa to nawet w przypadku niestandardowego tekstu zawierającego surowy link. Pamiętaj, aby wybrać Zachowanie -> Wybieralne i Wykrywanie -> Łącza.
krlbsk
3

Wersja szybka:

    // 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
źródło
3

Użyj UITextView i ustaw dataDetectorTypes dla Link.

lubię to:

testTextView.editable = false 
testTextView.dataDetectorTypes = .link

Jeśli chcesz wykryć link, numer telefonu, adres itp. Następnie

testTextView.dataDetectorTypes = .all
Adarsh ​​GJ
źródło
3
Nie. To pozwala tylko klikać linki. Moje pytanie dotyczy konkretnego kliknięcia dowolnego tekstu, takiego jak „kliknij tutaj”, a nie adresu URL takiego jakhttp://somedomain/someurl?param=value
Duncan C
2

Szybki dodatek do oryginalnego opisu Duncana C w stosunku do zachowania IB. Pisze: „To proste, aby hiperłącza można było klikać w UITextView. Po prostu ustawiłeś pole wyboru„ wykrywaj linki ”w widoku w IB, a on wykrywa łącza http i zamienia je w hiperłącza”.

Moje doświadczenie (przynajmniej w xcode 7) polega na tym, że musisz również odznaczyć zachowanie „Edytowalne”, aby adresy URL były wykrywane i klikalne.

sakumatto
źródło
2

Jeśli masz problemy z tym, co @Karl Nosworthy i @esilver podali powyżej, zaktualizowałem rozszerzenie NSMutableAttributString do wersji 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
źródło
0

Jeśli chcesz użyć NSLinkAttributeName w UITextView, możesz rozważyć użycie biblioteki AttributeTextView. Jest to podklasa UITextView, która sprawia, że ​​bardzo łatwo sobie z nimi poradzić. Aby uzyskać więcej informacji, zobacz: https://github.com/evermeer/AttributTextView

Możesz sprawić, by jakakolwiek część tekstu współdziałała w ten sposób (gdzie textView1 to 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) 

Do obsługi hashtagów i wzmianek możesz użyć takiego kodu:

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
źródło
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;

KLUCZOWE PUNKTY:

  • Upewnij się, że w XIB włączono „wybieralne” zachowanie UITextView.
  • Upewnij się, że wyłączyłeś zachowanie „edytowalne” UITextView w XIB.
Shashank Sharma
źródło